blob: e475f22d771df6d0f69d43fb5505e1dcbe31e08e [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 Bonadei8cc66952020-10-30 10:13:45 +010018 'api/video_codecs/video_decoder.h',
19 'common_types.cc',
20 'common_types.h',
21 'examples/objc',
22 'media/base/stream_params.h',
23 'media/base/video_common.h',
Florent Castelli22379fc2021-04-08 15:06:09 +020024 'media/sctp/usrsctp_transport.cc',
Mirko Bonadei8cc66952020-10-30 10:13:45 +010025 'modules/audio_coding',
26 'modules/audio_device',
27 'modules/audio_processing',
28 'modules/desktop_capture',
29 'modules/include/module_common_types.h',
30 'modules/utility',
31 'modules/video_capture',
32 'p2p/base/pseudo_tcp.cc',
33 'p2p/base/pseudo_tcp.h',
34 'rtc_base',
35 'sdk/android/src/jni',
36 'sdk/objc',
37 'system_wrappers',
38 'test',
39 'tools_webrtc',
40 '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 = [
Mirko Bonadei8cc66952020-10-30 10:13:45 +010055 '-build/c++11',
56 '-runtime/references',
57 '-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 = (
Mirko Bonadei8cc66952020-10-30 10:13:45 +010068 'api', # All subdirectories of api/ are included as well.
69 'media/base',
70 'media/engine',
71 '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 Bonadei8cc66952020-10-30 10:13:45 +010078 'common_audio/include',
79 'modules/audio_coding/include',
80 'modules/audio_processing/include',
81 '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',
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.
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200100TARGET_RE = re.compile(
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100101 r'(?P<indent>\s*)(?P<target_type>\w+)\("(?P<target_name>\w+)"\) {'
102 r'(?P<target_contents>.*?)'
103 r'(?P=indent)}', re.MULTILINE | re.DOTALL)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200104
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
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200109# DEPS_RE matches a block of sources inside a GN target.
110DEPS_RE = re.compile(r'\bdeps \+?= \[(?P<deps>.*?)\]',
111 re.MULTILINE | re.DOTALL)
112
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200113# FILE_PATH_RE matchies a file path.
114FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
115
kjellander53047c92015-12-02 23:56:14 -0800116
Mirko Bonadeid8665442018-09-04 12:17:27 +0200117def FindSrcDirPath(starting_dir):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100118 """Returns the abs path to the src/ dir of the project."""
119 src_dir = starting_dir
120 while os.path.basename(src_dir) != 'src':
121 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
122 return src_dir
Mirko Bonadeid8665442018-09-04 12:17:27 +0200123
124
Oleh Prypin2f33a562017-10-04 20:17:54 +0200125@contextmanager
126def _AddToPath(*paths):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100127 original_sys_path = sys.path
128 sys.path.extend(paths)
129 try:
130 yield
131 finally:
132 # Restore sys.path to what it was before.
133 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 05:27:22 -0800134
135
charujain9893e252017-09-14 13:33:22 +0200136def VerifyNativeApiHeadersListIsValid(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100137 """Ensures the list of native API header directories is up to date."""
138 non_existing_paths = []
139 native_api_full_paths = [
140 input_api.os_path.join(input_api.PresubmitLocalPath(),
141 *path.split('/')) for path in API_DIRS
142 ]
143 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 [
148 output_api.PresubmitError(
149 'Directories to native API headers have changed which has made the '
150 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
151 'location of our native APIs.', non_existing_paths)
152 ]
153 return []
kjellander53047c92015-12-02 23:56:14 -0800154
Artem Titove92675b2018-05-22 10:21:27 +0200155
kjellanderc88b5d52017-04-05 06:42:43 -0700156API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700157You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800158 1. Make compatible changes that don't break existing clients. Usually
159 this is done by keeping the existing method signatures unchanged.
Danil Chapovalov7013b3b2021-02-22 14:31:26 +0100160 2. Mark the old stuff as deprecated (use the ABSL_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700161 3. Create a timeline and plan for when the deprecated stuff will be
162 removed. (The amount of time we give users to change their code
163 should be informed by how much work it is for them. If they just
164 need to replace one name with another or something equally
165 simple, 1-2 weeks might be good; if they need to do serious work,
166 up to 3 months may be called for.)
167 4. Update/inform existing downstream code owners to stop using the
168 deprecated stuff. (Send announcements to
169 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
170 5. Remove the deprecated stuff, once the agreed-upon amount of time
171 has passed.
172Related files:
173"""
kjellander53047c92015-12-02 23:56:14 -0800174
Artem Titove92675b2018-05-22 10:21:27 +0200175
charujain9893e252017-09-14 13:33:22 +0200176def CheckNativeApiHeaderChanges(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100177 """Checks to remind proper changing of native APIs."""
178 files = []
179 source_file_filter = lambda x: input_api.FilterSourceFile(
180 x, files_to_check=[r'.+\.(gn|gni|h)$'])
181 for f in input_api.AffectedSourceFiles(source_file_filter):
182 for path in API_DIRS:
183 dn = os.path.dirname(f.LocalPath())
184 if path == 'api':
185 # Special case: Subdirectories included.
186 if dn == 'api' or dn.startswith('api/'):
187 files.append(f.LocalPath())
188 else:
189 # Normal case: Subdirectories not included.
190 if dn == path:
191 files.append(f.LocalPath())
kjellander53047c92015-12-02 23:56:14 -0800192
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100193 if files:
194 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
195 return []
kjellander53047c92015-12-02 23:56:14 -0800196
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100197
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100198def CheckNoIOStreamInHeaders(input_api, output_api, source_file_filter):
199 """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)
203 file_filter = lambda x: (input_api.FilterSourceFile(x) and
204 source_file_filter(x))
205 for f in input_api.AffectedSourceFiles(file_filter):
206 if not f.LocalPath().endswith('.h'):
207 continue
208 contents = input_api.ReadFile(f)
209 if pattern.search(contents):
210 files.append(f)
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000211
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100212 if len(files):
213 return [
214 output_api.PresubmitError(
215 'Do not #include <iostream> in header files, since it inserts static '
216 +
217 'initialization into every file including the header. Instead, '
218 + '#include <ostream>. See http://crbug.com/94794', files)
219 ]
220 return []
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000221
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000222
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100223def CheckNoPragmaOnce(input_api, output_api, source_file_filter):
224 """Make sure that banned functions are not used."""
225 files = []
226 pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE)
227 file_filter = lambda x: (input_api.FilterSourceFile(x) and
228 source_file_filter(x))
229 for f in input_api.AffectedSourceFiles(file_filter):
230 if not f.LocalPath().endswith('.h'):
231 continue
232 contents = input_api.ReadFile(f)
233 if pattern.search(contents):
234 files.append(f)
kjellander6aeef742017-02-20 01:13:18 -0800235
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100236 if files:
237 return [
238 output_api.PresubmitError(
239 'Do not use #pragma once in header files.\n'
240 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
241 files)
242 ]
243 return []
kjellander6aeef742017-02-20 01:13:18 -0800244
245
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100246def CheckNoFRIEND_TEST(
247 input_api,
248 output_api, # pylint: disable=invalid-name
249 source_file_filter):
250 """Make sure that gtest's FRIEND_TEST() macro is not used, the
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000251 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
252 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100253 problems = []
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000254
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100255 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
256 source_file_filter(f))
257 for f in input_api.AffectedFiles(file_filter=file_filter):
258 for line_num, line in f.ChangedContents():
259 if 'FRIEND_TEST(' in line:
260 problems.append(' %s:%d' % (f.LocalPath(), line_num))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000261
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100262 if not problems:
263 return []
264 return [
265 output_api.PresubmitPromptWarning(
266 'WebRTC\'s code should not use '
267 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
268 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))
269 ]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000270
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000271
Mirko Bonadeifc17a782020-06-30 14:31:37 +0200272def IsLintDisabled(disabled_paths, file_path):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100273 """ Checks if a file is disabled for lint check."""
274 for path in disabled_paths:
275 if file_path == path or os.path.dirname(file_path).startswith(path):
276 return True
277 return False
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100278
279
charujain9893e252017-09-14 13:33:22 +0200280def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 11:23:00 +0200281 source_file_filter=None):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100282 """Checks that all new or non-exempt .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200283 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000284 depot_tools/presubmit_canned_checks.py but has less filters and only checks
285 added files."""
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100286 result = []
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000287
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100288 # Initialize cpplint.
289 import cpplint
290 # Access to a protected member _XX of a client class
291 # pylint: disable=W0212
292 cpplint._cpplint_state.ResetErrorCounts()
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000293
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100294 lint_filters = cpplint._Filters()
295 lint_filters.extend(DISABLED_LINT_FILTERS)
296 cpplint._SetFilters(','.join(lint_filters))
jbauchc4e3ead2016-02-19 00:25:55 -0800297
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100298 # Create a platform independent exempt list for cpplint.
299 disabled_paths = [
300 input_api.os_path.join(*path.split('/')) for path in CPPLINT_EXCEPTIONS
301 ]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100302
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100303 # Use the strictest verbosity level for cpplint.py (level 1) which is the
304 # default when running cpplint.py from command line. To make it possible to
305 # work with not-yet-converted code, we're only applying it to new (or
306 # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS.
307 verbosity_level = 1
308 files = []
309 for f in input_api.AffectedSourceFiles(source_file_filter):
310 # Note that moved/renamed files also count as added.
311 if f.Action() == 'A' or not IsLintDisabled(disabled_paths,
312 f.LocalPath()):
313 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000314
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100315 for file_name in files:
316 cpplint.ProcessFile(file_name, verbosity_level)
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000317
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100318 if cpplint._cpplint_state.error_count > 0:
319 if input_api.is_committing:
320 res_type = output_api.PresubmitError
321 else:
322 res_type = output_api.PresubmitPromptWarning
323 result = [res_type('Changelist failed cpplint.py check.')]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000324
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100325 return result
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000326
Artem Titove92675b2018-05-22 10:21:27 +0200327
charujain9893e252017-09-14 13:33:22 +0200328def CheckNoSourcesAbove(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100329 # Disallow referencing source files with paths above the GN file location.
330 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
331 re.MULTILINE | re.DOTALL)
332 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
333 violating_gn_files = set()
334 violating_source_entries = []
335 for gn_file in gn_files:
336 contents = input_api.ReadFile(gn_file)
337 for source_block_match in source_pattern.finditer(contents):
338 # Find all source list entries starting with ../ in the source block
339 # (exclude overrides entries).
340 for file_list_match in file_pattern.finditer(
341 source_block_match.group(1)):
342 source_file = file_list_match.group(1)
343 if 'overrides/' not in source_file:
344 violating_source_entries.append(source_file)
345 violating_gn_files.add(gn_file)
346 if violating_gn_files:
347 return [
348 output_api.PresubmitError(
349 'Referencing source files above the directory of the GN file is not '
350 'allowed. Please introduce new GN targets in the proper location '
351 'instead.\n'
352 'Invalid source entries:\n'
353 '%s\n'
354 'Violating GN files:' % '\n'.join(violating_source_entries),
355 items=violating_gn_files)
356 ]
357 return []
ehmaldonado5b1ba082016-09-02 05:51:08 -0700358
Artem Titove92675b2018-05-22 10:21:27 +0200359
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200360def CheckAbseilDependencies(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100361 """Checks that Abseil dependencies are declared in `absl_deps`."""
362 absl_re = re.compile(r'third_party/abseil-cpp', re.MULTILINE | re.DOTALL)
363 target_types_to_check = [
364 'rtc_library',
365 'rtc_source_set',
366 'rtc_static_library',
367 'webrtc_fuzzer_test',
368 ]
369 error_msg = ('Abseil dependencies in target "%s" (file: %s) '
370 'should be moved to the "absl_deps" parameter.')
371 errors = []
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200372
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100373 for gn_file in gn_files:
374 gn_file_content = input_api.ReadFile(gn_file)
375 for target_match in TARGET_RE.finditer(gn_file_content):
376 target_type = target_match.group('target_type')
377 target_name = target_match.group('target_name')
378 target_contents = target_match.group('target_contents')
379 if target_type in target_types_to_check:
380 for deps_match in DEPS_RE.finditer(target_contents):
381 deps = deps_match.group('deps').splitlines()
382 for dep in deps:
383 if re.search(absl_re, dep):
384 errors.append(
385 output_api.PresubmitError(
386 error_msg %
387 (target_name, gn_file.LocalPath())))
388 break # no need to warn more than once per target
389 return errors
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200390
391
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200392def CheckNoMixingSources(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100393 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200394
395 See bugs.webrtc.org/7743 for more context.
396 """
Artem Titove92675b2018-05-22 10:21:27 +0200397
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100398 def _MoreThanOneSourceUsed(*sources_lists):
399 sources_used = 0
400 for source_list in sources_lists:
401 if len(source_list):
402 sources_used += 1
403 return sources_used > 1
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200404
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100405 errors = defaultdict(lambda: [])
406 for gn_file in gn_files:
407 gn_file_content = input_api.ReadFile(gn_file)
408 for target_match in TARGET_RE.finditer(gn_file_content):
409 # list_of_sources is a list of tuples of the form
410 # (c_files, cc_files, objc_files) that keeps track of all the sources
411 # defined in a target. A GN target can have more that on definition of
412 # sources (since it supports if/else statements).
413 # E.g.:
414 # rtc_static_library("foo") {
415 # if (is_win) {
416 # sources = [ "foo.cc" ]
417 # } else {
418 # sources = [ "foo.mm" ]
419 # }
420 # }
421 # This is allowed and the presubmit check should support this case.
422 list_of_sources = []
423 c_files = []
424 cc_files = []
425 objc_files = []
426 target_name = target_match.group('target_name')
427 target_contents = target_match.group('target_contents')
428 for sources_match in SOURCES_RE.finditer(target_contents):
429 if '+=' not in sources_match.group(0):
430 if c_files or cc_files or objc_files:
431 list_of_sources.append((c_files, cc_files, objc_files))
432 c_files = []
433 cc_files = []
434 objc_files = []
435 for file_match in FILE_PATH_RE.finditer(
436 sources_match.group(1)):
437 file_path = file_match.group('file_path')
438 extension = file_match.group('extension')
439 if extension == '.c':
440 c_files.append(file_path + extension)
441 if extension == '.cc':
442 cc_files.append(file_path + extension)
443 if extension in ['.m', '.mm']:
444 objc_files.append(file_path + extension)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200445 list_of_sources.append((c_files, cc_files, objc_files))
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100446 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
447 if _MoreThanOneSourceUsed(c_files_list, cc_files_list,
448 objc_files_list):
449 all_sources = sorted(c_files_list + cc_files_list +
450 objc_files_list)
451 errors[gn_file.LocalPath()].append(
452 (target_name, all_sources))
453 if errors:
454 return [
455 output_api.PresubmitError(
456 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
457 'Please create a separate target for each collection of sources.\n'
458 'Mixed sources: \n'
459 '%s\n'
460 'Violating GN files:\n%s\n' %
461 (json.dumps(errors, indent=2), '\n'.join(errors.keys())))
462 ]
463 return []
kjellander7439f972016-12-05 22:47:46 -0800464
Artem Titove92675b2018-05-22 10:21:27 +0200465
charujain9893e252017-09-14 13:33:22 +0200466def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100467 cwd = input_api.PresubmitLocalPath()
468 with _AddToPath(
469 input_api.os_path.join(cwd, 'tools_webrtc',
470 'presubmit_checks_lib')):
471 from check_package_boundaries import CheckPackageBoundaries
472 build_files = [
473 os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files
474 ]
475 errors = CheckPackageBoundaries(cwd, build_files)[:5]
476 if errors:
477 return [
478 output_api.PresubmitError(
479 'There are package boundary violations in the following GN files:',
480 long_text='\n\n'.join(str(err) for err in errors))
481 ]
482 return []
ehmaldonado4fb97462017-01-30 05:27:22 -0800483
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100484
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200485def _ReportFileAndLine(filename, line_num):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100486 """Default error formatter for _FindNewViolationsOfRule."""
487 return '%s (line %s)' % (filename, line_num)
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100488
489
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100490def CheckNoWarningSuppressionFlagsAreAdded(gn_files,
491 input_api,
492 output_api,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200493 error_formatter=_ReportFileAndLine):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100494 """Make sure that warning suppression flags are not added wihtout a reason."""
495 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
496 'in WebRTC.\n'
497 'If you are not adding this code (e.g. you are just moving '
498 'existing code) or you want to add an exception,\n'
499 'you can add a comment on the line that causes the problem:\n\n'
500 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
501 '\n'
502 'Affected files:\n')
503 errors = [] # 2-element tuples with (file, line number)
504 clang_warn_re = input_api.re.compile(
505 r'//build/config/clang:extra_warnings')
506 no_presubmit_re = input_api.re.compile(
507 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
508 for f in gn_files:
509 for line_num, line in f.ChangedContents():
510 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
511 errors.append(error_formatter(f.LocalPath(), line_num))
512 if errors:
513 return [output_api.PresubmitError(msg, errors)]
514 return []
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200515
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100516
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100517def CheckNoTestCaseUsageIsAdded(input_api,
518 output_api,
519 source_file_filter,
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100520 error_formatter=_ReportFileAndLine):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100521 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
522 'new API: https://github.com/google/googletest/blob/master/'
523 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
524 'Affected files:\n')
525 errors = [] # 2-element tuples with (file, line number)
526 test_case_re = input_api.re.compile(r'TEST_CASE')
527 file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith(
528 '.cc'))
529 for f in input_api.AffectedSourceFiles(file_filter):
530 for line_num, line in f.ChangedContents():
531 if test_case_re.search(line):
532 errors.append(error_formatter(f.LocalPath(), line_num))
533 if errors:
534 return [output_api.PresubmitError(error_msg, errors)]
535 return []
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100536
537
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100538def CheckNoStreamUsageIsAdded(input_api,
539 output_api,
Artem Titov739351d2018-05-11 12:21:36 +0200540 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200541 error_formatter=_ReportFileAndLine):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100542 """Make sure that no more dependencies on stringstream are added."""
543 error_msg = (
544 'Usage of <sstream>, <istream> and <ostream> in WebRTC is '
545 'deprecated.\n'
546 'This includes the following types:\n'
547 'std::istringstream, std::ostringstream, std::wistringstream, '
548 'std::wostringstream,\n'
549 'std::wstringstream, std::ostream, std::wostream, std::istream,'
550 'std::wistream,\n'
551 'std::iostream, std::wiostream.\n'
552 'If you are not adding this code (e.g. you are just moving '
553 'existing code),\n'
554 'you can add a comment on the line that causes the problem:\n\n'
555 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
556 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
557 '\n'
558 'If you are adding new code, consider using '
559 'rtc::SimpleStringBuilder\n'
560 '(in rtc_base/strings/string_builder.h).\n'
561 'Affected files:\n')
562 errors = [] # 2-element tuples with (file, line number)
563 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
564 usage_re = input_api.re.compile(
565 r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
566 no_presubmit_re = input_api.re.compile(
567 r'// no-presubmit-check TODO\(webrtc:8982\)')
568 file_filter = lambda x: (input_api.FilterSourceFile(x) and
569 source_file_filter(x))
Mirko Bonadei571791a2019-05-07 14:08:05 +0200570
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100571 def _IsException(file_path):
572 is_test = any(
573 file_path.endswith(x) for x in
574 ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc'])
575 return (file_path.startswith('examples')
576 or file_path.startswith('test') or is_test)
Patrik Höglund2ea27962020-01-13 15:10:40 +0100577
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100578 for f in input_api.AffectedSourceFiles(file_filter):
579 # Usage of stringstream is allowed under examples/ and in tests.
580 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
581 continue
582 for line_num, line in f.ChangedContents():
583 if ((include_re.search(line) or usage_re.search(line))
584 and not no_presubmit_re.search(line)):
585 errors.append(error_formatter(f.LocalPath(), line_num))
586 if errors:
587 return [output_api.PresubmitError(error_msg, errors)]
588 return []
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100589
Artem Titove92675b2018-05-22 10:21:27 +0200590
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200591def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100592 """Checks that public_deps is not used without a good reason."""
593 result = []
594 no_presubmit_check_re = input_api.re.compile(
595 r'# no-presubmit-check TODO\(webrtc:\d+\)')
596 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
597 'because it doesn\'t map well to downstream build systems.\n'
598 'Used in: %s (line %d).\n'
599 'If you are not adding this code (e.g. you are just moving '
600 'existing code) or you have a good reason, you can add this '
601 'comment (verbatim) on the line that causes the problem:\n\n'
602 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
603 for affected_file in gn_files:
604 for (line_number, affected_line) in affected_file.ChangedContents():
605 if 'public_deps' in affected_line:
606 surpressed = no_presubmit_check_re.search(affected_line)
607 if not surpressed:
608 result.append(
609 output_api.PresubmitError(
610 error_msg %
611 (affected_file.LocalPath(), line_number)))
612 return result
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100613
Artem Titove92675b2018-05-22 10:21:27 +0200614
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700615def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100616 result = []
617 error_msg = (
618 'check_includes overrides are not allowed since it can cause '
619 'incorrect dependencies to form. It effectively means that your '
620 'module can include any .h file without depending on its '
621 'corresponding target. There are some exceptional cases when '
622 'this is allowed: if so, get approval from a .gn owner in the '
623 'root OWNERS file.\n'
624 'Used in: %s (line %d).')
625 no_presubmit_re = input_api.re.compile(
626 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
627 for affected_file in gn_files:
628 for (line_number, affected_line) in affected_file.ChangedContents():
629 if ('check_includes' in affected_line
630 and not no_presubmit_re.search(affected_line)):
631 result.append(
632 output_api.PresubmitError(
633 error_msg % (affected_file.LocalPath(), line_number)))
634 return result
Patrik Höglund6f491062018-01-11 12:04:23 +0100635
Artem Titove92675b2018-05-22 10:21:27 +0200636
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200637def CheckGnChanges(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100638 file_filter = lambda x: (input_api.FilterSourceFile(
639 x,
640 files_to_check=(r'.+\.(gn|gni)$', ),
641 files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', )))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700642
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100643 gn_files = []
644 for f in input_api.AffectedSourceFiles(file_filter):
645 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700646
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100647 result = []
648 if gn_files:
649 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
650 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
651 result.extend(CheckAbseilDependencies(input_api, gn_files, output_api))
652 result.extend(
653 CheckNoPackageBoundaryViolations(input_api, gn_files, output_api))
654 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api,
655 output_api))
656 result.extend(
657 CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
658 result.extend(
659 CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200660 output_api))
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100661 return result
ehmaldonado5b1ba082016-09-02 05:51:08 -0700662
Artem Titove92675b2018-05-22 10:21:27 +0200663
Oleh Prypin920b6532017-10-05 11:28:51 +0200664def CheckGnGen(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100665 """Runs `gn gen --check` with default args to detect mismatches between
Oleh Prypin920b6532017-10-05 11:28:51 +0200666 #includes and dependencies in the BUILD.gn files, as well as general build
667 errors.
668 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100669 with _AddToPath(
670 input_api.os_path.join(input_api.PresubmitLocalPath(),
671 'tools_webrtc', 'presubmit_checks_lib')):
672 from build_helpers import RunGnCheck
673 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
674 if errors:
675 return [
676 output_api.PresubmitPromptWarning(
677 'Some #includes do not match the build dependency graph. Please run:\n'
678 ' gn gen --check <out_dir>',
679 long_text='\n\n'.join(errors))
680 ]
681 return []
Oleh Prypin920b6532017-10-05 11:28:51 +0200682
Artem Titove92675b2018-05-22 10:21:27 +0200683
Artem Titova04d1402018-05-11 11:23:00 +0200684def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100685 """Runs checkdeps on #include statements added in this
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000686 change. Breaking - rules is an error, breaking ! rules is a
687 warning.
688 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100689 # Copied from Chromium's src/PRESUBMIT.py.
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000690
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100691 # We need to wait until we have an input_api object and use this
692 # roundabout construct to import checkdeps because this file is
693 # eval-ed and thus doesn't have __file__.
694 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
695 checkdeps_path = input_api.os_path.join(src_path, 'buildtools',
696 'checkdeps')
697 if not os.path.exists(checkdeps_path):
698 return [
699 output_api.PresubmitError(
700 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
701 'download all the DEPS entries?' % checkdeps_path)
702 ]
703 with _AddToPath(checkdeps_path):
704 import checkdeps
705 from cpp_checker import CppChecker
706 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000707
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100708 added_includes = []
709 for f in input_api.AffectedFiles(file_filter=source_file_filter):
710 if not CppChecker.IsCppFile(f.LocalPath()):
711 continue
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000712
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100713 changed_lines = [line for _, line in f.ChangedContents()]
714 added_includes.append([f.LocalPath(), changed_lines])
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000715
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100716 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000717
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100718 error_descriptions = []
719 warning_descriptions = []
720 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
721 added_includes):
722 description_with_path = '%s\n %s' % (path, rule_description)
723 if rule_type == Rule.DISALLOW:
724 error_descriptions.append(description_with_path)
725 else:
726 warning_descriptions.append(description_with_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000727
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100728 results = []
729 if error_descriptions:
730 results.append(
731 output_api.PresubmitError(
732 'You added one or more #includes that violate checkdeps rules.\n'
733 'Check that the DEPS files in these locations contain valid rules.\n'
734 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
735 'more details about checkdeps.', error_descriptions))
736 if warning_descriptions:
737 results.append(
738 output_api.PresubmitPromptOrNotify(
739 'You added one or more #includes of files that are temporarily\n'
740 'allowed but being removed. Can you avoid introducing the\n'
741 '#include? See relevant DEPS file(s) for details and contacts.\n'
742 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
743 'more details about checkdeps.', warning_descriptions))
744 return results
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000745
Artem Titove92675b2018-05-22 10:21:27 +0200746
charujain9893e252017-09-14 13:33:22 +0200747def CheckCommitMessageBugEntry(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100748 """Check that bug entries are well-formed in commit message."""
749 bogus_bug_msg = (
750 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
751 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.'
752 )
753 results = []
754 for bug in input_api.change.BugsFromDescription():
755 bug = bug.strip()
756 if bug.lower() == 'none':
757 continue
758 if 'b/' not in bug and ':' not in bug:
759 try:
760 if int(bug) > 100000:
761 # Rough indicator for current chromium bugs.
762 prefix_guess = 'chromium'
763 else:
764 prefix_guess = 'webrtc'
765 results.append(
766 'Bug entry requires issue tracker prefix, e.g. %s:%s' %
767 (prefix_guess, bug))
768 except ValueError:
769 results.append(bogus_bug_msg % bug)
770 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
771 results.append(bogus_bug_msg % bug)
772 return [output_api.PresubmitError(r) for r in results]
charujain9893e252017-09-14 13:33:22 +0200773
Artem Titove92675b2018-05-22 10:21:27 +0200774
charujain9893e252017-09-14 13:33:22 +0200775def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100776 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700777
778 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200779 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700780 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200781
782 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
783 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700784 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100785 if input_api.change.BugsFromDescription():
786 return []
787 else:
788 return [
789 output_api.PresubmitError(
790 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
791 'reference it using either of:\n'
792 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
793 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX'
794 )
795 ]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000796
Artem Titove92675b2018-05-22 10:21:27 +0200797
Artem Titova04d1402018-05-11 11:23:00 +0200798def CheckJSONParseErrors(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100799 """Check that JSON files do not contain syntax errors."""
kjellander569cf942016-02-11 05:02:59 -0800800
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100801 def FilterFile(affected_file):
802 return (input_api.os_path.splitext(
803 affected_file.LocalPath())[1] == '.json'
804 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800805
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100806 def GetJSONParseError(input_api, filename):
807 try:
808 contents = input_api.ReadFile(filename)
809 input_api.json.loads(contents)
810 except ValueError as e:
811 return e
812 return None
kjellander569cf942016-02-11 05:02:59 -0800813
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100814 results = []
815 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
816 include_deletes=False):
817 parse_error = GetJSONParseError(input_api,
818 affected_file.AbsoluteLocalPath())
819 if parse_error:
820 results.append(
821 output_api.PresubmitError(
822 '%s could not be parsed: %s' %
823 (affected_file.LocalPath(), parse_error)))
824 return results
kjellander569cf942016-02-11 05:02:59 -0800825
826
charujain9893e252017-09-14 13:33:22 +0200827def RunPythonTests(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100828 def Join(*args):
829 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200830
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100831 test_directories = [
832 input_api.PresubmitLocalPath(),
833 Join('rtc_tools', 'py_event_log_analyzer'),
834 Join('audio', 'test', 'unittests'),
835 ] + [
836 root for root, _, files in os.walk(Join('tools_webrtc')) if any(
837 f.endswith('_test.py') for f in files)
838 ]
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200839
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100840 tests = []
841 for directory in test_directories:
842 tests.extend(
843 input_api.canned_checks.GetUnitTestsInDirectory(
844 input_api,
845 output_api,
846 directory,
847 files_to_check=[r'.+_test\.py$']))
848 return input_api.RunTests(tests, parallel=True)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200849
850
Artem Titova04d1402018-05-11 11:23:00 +0200851def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
852 source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100853 """Checks that the namespace google::protobuf has not been used."""
854 files = []
855 pattern = input_api.re.compile(r'google::protobuf')
856 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
857 file_filter = lambda x: (input_api.FilterSourceFile(x) and
858 source_file_filter(x))
859 for f in input_api.AffectedSourceFiles(file_filter):
860 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
861 continue
862 contents = input_api.ReadFile(f)
863 if pattern.search(contents):
864 files.append(f)
mbonadei38415b22017-04-07 05:38:01 -0700865
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100866 if files:
867 return [
868 output_api.PresubmitError(
869 'Please avoid to use namespace `google::protobuf` directly.\n'
870 'Add a using directive in `%s` and include that header instead.'
871 % proto_utils_path, files)
872 ]
873 return []
mbonadei38415b22017-04-07 05:38:01 -0700874
875
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200876def _LicenseHeader(input_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100877 """Returns the license header regexp."""
878 # Accept any year number from 2003 to the current year
879 current_year = int(input_api.time.strftime('%Y'))
880 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
881 years_re = '(' + '|'.join(allowed_years) + ')'
882 license_header = (
883 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200884 r'All [Rr]ights [Rr]eserved\.\n'
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100885 r'.*?\n'
886 r'.*? Use of this source code is governed by a BSD-style license\n'
887 r'.*? that can be found in the LICENSE file in the root of the source\n'
888 r'.*? tree\. An additional intellectual property rights grant can be '
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200889 r'found\n'
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100890 r'.*? in the file PATENTS\. All contributing project authors may\n'
891 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
892 ) % {
893 'year': years_re,
894 }
895 return license_header
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200896
897
charujain9893e252017-09-14 13:33:22 +0200898def CommonChecks(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100899 """Checks common to both upload and commit."""
900 results = []
901 # Filter out files that are in objc or ios dirs from being cpplint-ed since
902 # they do not follow C++ lint rules.
903 exception_list = input_api.DEFAULT_FILES_TO_SKIP + (
904 r".*\bobjc[\\\/].*",
905 r".*objc\.[hcm]+$",
906 )
907 source_file_filter = lambda x: input_api.FilterSourceFile(
908 x, None, exception_list)
909 results.extend(
910 CheckApprovedFilesLintClean(input_api, output_api, source_file_filter))
911 results.extend(
912 input_api.canned_checks.CheckLicense(input_api, output_api,
913 _LicenseHeader(input_api)))
914 results.extend(
915 input_api.canned_checks.RunPylint(
916 input_api,
917 output_api,
918 files_to_skip=(
919 r'^base[\\\/].*\.py$',
920 r'^build[\\\/].*\.py$',
921 r'^buildtools[\\\/].*\.py$',
922 r'^infra[\\\/].*\.py$',
923 r'^ios[\\\/].*\.py$',
924 r'^out.*[\\\/].*\.py$',
925 r'^testing[\\\/].*\.py$',
926 r'^third_party[\\\/].*\.py$',
927 r'^tools[\\\/].*\.py$',
928 # TODO(phoglund): should arguably be checked.
929 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
930 r'^xcodebuild.*[\\\/].*\.py$',
931 ),
932 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800933
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100934 # TODO(nisse): talk/ is no more, so make below checks simpler?
935 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
936 # we need to have different license checks in talk/ and webrtc/ directories.
937 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200938
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100939 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
940 # ObjC subdirectories ObjC headers.
941 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
942 # Skip long-lines check for DEPS and GN files.
943 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
944 # Also we will skip most checks for third_party directory.
945 third_party_filter_list = (r'^third_party[\\\/].+', )
946 eighty_char_sources = lambda x: input_api.FilterSourceFile(
947 x,
948 files_to_skip=build_file_filter_list + objc_filter_list +
949 third_party_filter_list)
950 hundred_char_sources = lambda x: input_api.FilterSourceFile(
951 x, files_to_check=objc_filter_list)
952 non_third_party_sources = lambda x: input_api.FilterSourceFile(
953 x, files_to_skip=third_party_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200954
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100955 results.extend(
956 input_api.canned_checks.CheckLongLines(
957 input_api,
958 output_api,
959 maxlen=80,
960 source_file_filter=eighty_char_sources))
961 results.extend(
962 input_api.canned_checks.CheckLongLines(
963 input_api,
964 output_api,
965 maxlen=100,
966 source_file_filter=hundred_char_sources))
967 results.extend(
968 input_api.canned_checks.CheckChangeHasNoTabs(
969 input_api, output_api, source_file_filter=non_third_party_sources))
970 results.extend(
971 input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
972 input_api, output_api, source_file_filter=non_third_party_sources))
973 results.extend(
974 input_api.canned_checks.CheckAuthorizedAuthor(
975 input_api,
976 output_api,
977 bot_allowlist=[
Mirko Bonadei39423802020-12-15 12:02:26 +0100978 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com',
979 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com',
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100980 ]))
981 results.extend(
982 input_api.canned_checks.CheckChangeTodoHasOwner(
983 input_api, output_api, source_file_filter=non_third_party_sources))
984 results.extend(
985 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
986 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
987 results.extend(
988 CheckNoIOStreamInHeaders(input_api,
989 output_api,
990 source_file_filter=non_third_party_sources))
991 results.extend(
992 CheckNoPragmaOnce(input_api,
993 output_api,
994 source_file_filter=non_third_party_sources))
995 results.extend(
996 CheckNoFRIEND_TEST(input_api,
997 output_api,
998 source_file_filter=non_third_party_sources))
999 results.extend(CheckGnChanges(input_api, output_api))
1000 results.extend(
1001 CheckUnwantedDependencies(input_api,
1002 output_api,
1003 source_file_filter=non_third_party_sources))
1004 results.extend(
1005 CheckJSONParseErrors(input_api,
1006 output_api,
1007 source_file_filter=non_third_party_sources))
1008 results.extend(RunPythonTests(input_api, output_api))
1009 results.extend(
1010 CheckUsageOfGoogleProtobufNamespace(
1011 input_api, output_api, source_file_filter=non_third_party_sources))
1012 results.extend(
1013 CheckOrphanHeaders(input_api,
1014 output_api,
1015 source_file_filter=non_third_party_sources))
1016 results.extend(
1017 CheckNewlineAtTheEndOfProtoFiles(
1018 input_api, output_api, source_file_filter=non_third_party_sources))
1019 results.extend(
1020 CheckNoStreamUsageIsAdded(input_api, output_api,
1021 non_third_party_sources))
1022 results.extend(
1023 CheckNoTestCaseUsageIsAdded(input_api, output_api,
1024 non_third_party_sources))
1025 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1026 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
1027 results.extend(
1028 CheckAbslMemoryInclude(input_api, output_api, non_third_party_sources))
1029 results.extend(
1030 CheckBannedAbslMakeUnique(input_api, output_api,
1031 non_third_party_sources))
1032 results.extend(
1033 CheckObjcApiSymbols(input_api, output_api, non_third_party_sources))
1034 return results
Mirko Bonadeia418e672018-10-24 13:57:25 +02001035
1036
1037def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001038 """Check that 'include_rules' in api/DEPS is up to date.
Mirko Bonadei90490372018-10-26 13:17:47 +02001039
1040 The file api/DEPS must be kept up to date in order to avoid to avoid to
1041 include internal header from WebRTC's api/ headers.
1042
1043 This check is focused on ensuring that 'include_rules' contains a deny
1044 rule for each root level directory. More focused allow rules can be
1045 added to 'specific_include_rules'.
1046 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001047 results = []
1048 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
1049 with open(api_deps) as f:
1050 deps_content = _ParseDeps(f.read())
Mirko Bonadeia418e672018-10-24 13:57:25 +02001051
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001052 include_rules = deps_content.get('include_rules', [])
1053 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 13:57:25 +02001054
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001055 # Only check top level directories affected by the current CL.
1056 dirs_to_check = set()
1057 for f in input_api.AffectedFiles():
1058 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
1059 if len(path_tokens) > 1:
1060 if (path_tokens[0] not in dirs_to_skip and os.path.isdir(
1061 os.path.join(input_api.PresubmitLocalPath(),
1062 path_tokens[0]))):
1063 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 13:57:25 +02001064
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001065 missing_include_rules = set()
1066 for p in dirs_to_check:
1067 rule = '-%s' % p
1068 if rule not in include_rules:
1069 missing_include_rules.add(rule)
Mirko Bonadei90490372018-10-26 13:17:47 +02001070
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001071 if missing_include_rules:
1072 error_msg = [
1073 'include_rules = [\n',
1074 ' ...\n',
1075 ]
Mirko Bonadeia418e672018-10-24 13:57:25 +02001076
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001077 for r in sorted(missing_include_rules):
1078 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 13:57:25 +02001079
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001080 error_msg.append(' ...\n')
1081 error_msg.append(']\n')
Mirko Bonadei90490372018-10-26 13:17:47 +02001082
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001083 results.append(
1084 output_api.PresubmitError(
1085 'New root level directory detected! WebRTC api/ headers should '
1086 'not #include headers from \n'
1087 'the new directory, so please update "include_rules" in file\n'
1088 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
Mirko Bonadei90490372018-10-26 13:17:47 +02001089
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001090 return results
1091
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001092
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001093def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001094 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
1095 source_file_filter(f))
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001096
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001097 files = []
1098 for f in input_api.AffectedFiles(include_deletes=False,
1099 file_filter=file_filter):
1100 for _, line in f.ChangedContents():
1101 if 'absl::make_unique' in line:
1102 files.append(f)
1103 break
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001104
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001105 if len(files):
1106 return [
1107 output_api.PresubmitError(
1108 'Please use std::make_unique instead of absl::make_unique.\n'
1109 'Affected files:', files)
1110 ]
1111 return []
1112
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001113
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001114def CheckObjcApiSymbols(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001115 rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}',
1116 re.MULTILINE | re.DOTALL)
1117 file_filter = lambda f: (f.LocalPath().endswith(('.h')) and
1118 source_file_filter(f))
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001119
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001120 files = []
1121 file_filter = lambda x: (input_api.FilterSourceFile(x) and
1122 source_file_filter(x))
1123 for f in input_api.AffectedSourceFiles(file_filter):
1124 if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath():
1125 continue
Niels Möller11ab77d2020-11-16 15:38:23 +01001126 if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'):
1127 continue
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001128 contents = input_api.ReadFile(f)
1129 for match in rtc_objc_export.finditer(contents):
1130 export_block = match.group(0)
1131 if 'RTC_OBJC_TYPE' not in export_block:
1132 files.append(f.LocalPath())
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001133
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001134 if len(files):
1135 return [
1136 output_api.PresubmitError(
1137 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() '
1138 + 'macro.\n\n' + 'For example:\n' +
1139 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' +
1140 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' +
1141 'Please fix the following files:', files)
1142 ]
1143 return []
1144
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001145
tzika06bf852018-11-15 20:37:35 +09001146def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001147 pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"',
1148 input_api.re.MULTILINE)
1149 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
1150 source_file_filter(f))
tzika06bf852018-11-15 20:37:35 +09001151
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001152 files = []
1153 for f in input_api.AffectedFiles(include_deletes=False,
1154 file_filter=file_filter):
1155 contents = input_api.ReadFile(f)
1156 if pattern.search(contents):
1157 continue
1158 for _, line in f.ChangedContents():
1159 if 'absl::WrapUnique' in line:
1160 files.append(f)
1161 break
tzika06bf852018-11-15 20:37:35 +09001162
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001163 if len(files):
1164 return [
1165 output_api.PresubmitError(
1166 'Please include "absl/memory/memory.h" header for absl::WrapUnique.\n'
1167 'This header may or may not be included transitively depending on the '
1168 'C++ standard version.', files)
1169 ]
1170 return []
1171
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001172
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001173def CheckChangeOnUpload(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001174 results = []
1175 results.extend(CommonChecks(input_api, output_api))
1176 results.extend(CheckGnGen(input_api, output_api))
1177 results.extend(
1178 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
1179 return results
niklase@google.comda159d62011-05-30 11:51:34 +00001180
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001181
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001182def CheckChangeOnCommit(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001183 results = []
1184 results.extend(CommonChecks(input_api, output_api))
1185 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
1186 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
1187 results.extend(
1188 input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api))
1189 results.extend(
1190 input_api.canned_checks.CheckChangeHasDescription(
1191 input_api, output_api))
1192 results.extend(CheckChangeHasBugField(input_api, output_api))
1193 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
1194 results.extend(
1195 input_api.canned_checks.CheckTreeIsOpen(
1196 input_api,
1197 output_api,
1198 json_url='http://webrtc-status.appspot.com/current?format=json'))
1199 return results
mbonadei74973ed2017-05-09 07:58:05 -07001200
1201
Artem Titova04d1402018-05-11 11:23:00 +02001202def CheckOrphanHeaders(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001203 # We need to wait until we have an input_api object and use this
1204 # roundabout construct to import prebubmit_checks_lib because this file is
1205 # eval-ed and thus doesn't have __file__.
1206 error_msg = """{} should be listed in {}."""
1207 results = []
1208 exempt_paths = [
1209 os.path.join('tools_webrtc', 'ios', 'SDK'),
1210 ]
1211 with _AddToPath(
1212 input_api.os_path.join(input_api.PresubmitLocalPath(),
1213 'tools_webrtc', 'presubmit_checks_lib')):
1214 from check_orphan_headers import GetBuildGnPathFromFilePath
1215 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -07001216
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001217 file_filter = lambda x: input_api.FilterSourceFile(
1218 x, files_to_skip=exempt_paths) and source_file_filter(x)
1219 for f in input_api.AffectedSourceFiles(file_filter):
1220 if f.LocalPath().endswith('.h'):
1221 file_path = os.path.abspath(f.LocalPath())
1222 root_dir = os.getcwd()
1223 gn_file_path = GetBuildGnPathFromFilePath(file_path,
1224 os.path.exists, root_dir)
1225 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1226 if not in_build_gn:
1227 results.append(
1228 output_api.PresubmitError(
1229 error_msg.format(f.LocalPath(),
1230 os.path.relpath(gn_file_path))))
1231 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001232
1233
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001234def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api,
1235 source_file_filter):
1236 """Checks that all .proto files are terminated with a newline."""
1237 error_msg = 'File {} must end with exactly one newline.'
1238 results = []
1239 file_filter = lambda x: input_api.FilterSourceFile(
1240 x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x)
1241 for f in input_api.AffectedSourceFiles(file_filter):
1242 file_path = f.LocalPath()
1243 with open(file_path) as f:
1244 lines = f.readlines()
1245 if len(lines) > 0 and not lines[-1].endswith('\n'):
1246 results.append(
1247 output_api.PresubmitError(error_msg.format(file_path)))
1248 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001249
1250
1251def _ExtractAddRulesFromParsedDeps(parsed_deps):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001252 """Extract the rules that add dependencies from a parsed DEPS file.
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001253
1254 Args:
1255 parsed_deps: the locals dictionary from evaluating the DEPS file."""
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001256 add_rules = set()
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001257 add_rules.update([
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001258 rule[1:] for rule in parsed_deps.get('include_rules', [])
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001259 if rule.startswith('+') or rule.startswith('!')
1260 ])
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001261 for _, rules in parsed_deps.get('specific_include_rules', {}).iteritems():
1262 add_rules.update([
1263 rule[1:] for rule in rules
1264 if rule.startswith('+') or rule.startswith('!')
1265 ])
1266 return add_rules
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001267
1268
1269def _ParseDeps(contents):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001270 """Simple helper for parsing DEPS files."""
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001271
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001272 # Stubs for handling special syntax in the root DEPS file.
1273 class VarImpl(object):
1274 def __init__(self, local_scope):
1275 self._local_scope = local_scope
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001276
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001277 def Lookup(self, var_name):
1278 """Implements the Var syntax."""
1279 try:
1280 return self._local_scope['vars'][var_name]
1281 except KeyError:
1282 raise Exception('Var is not defined: %s' % var_name)
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001283
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001284 local_scope = {}
1285 global_scope = {
1286 'Var': VarImpl(local_scope).Lookup,
1287 }
1288 exec contents in global_scope, local_scope
1289 return local_scope
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001290
1291
1292def _CalculateAddedDeps(os_path, old_contents, new_contents):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001293 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001294 a set of DEPS entries that we should look up.
1295
1296 For a directory (rather than a specific filename) we fake a path to
1297 a specific filename by adding /DEPS. This is chosen as a file that
1298 will seldom or never be subject to per-file include_rules.
1299 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001300 # We ignore deps entries on auto-generated directories.
1301 auto_generated_dirs = ['grit', 'jni']
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001302
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001303 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1304 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001305
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001306 added_deps = new_deps.difference(old_deps)
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001307
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001308 results = set()
1309 for added_dep in added_deps:
1310 if added_dep.split('/')[0] in auto_generated_dirs:
1311 continue
1312 # Assume that a rule that ends in .h is a rule for a specific file.
1313 if added_dep.endswith('.h'):
1314 results.add(added_dep)
1315 else:
1316 results.add(os_path.join(added_dep, 'DEPS'))
1317 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001318
1319
1320def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001321 """When a dependency prefixed with + is added to a DEPS file, we
Edward Lesmesbef08502021-02-05 14:08:32 -08001322 want to make sure that the change is reviewed by an OWNER of the
1323 target file or directory, to avoid layering violations from being
1324 introduced. This check verifies that this happens.
1325 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001326 virtual_depended_on_files = set()
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001327
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001328 file_filter = lambda f: not input_api.re.match(
1329 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1330 for f in input_api.AffectedFiles(include_deletes=False,
1331 file_filter=file_filter):
1332 filename = input_api.os_path.basename(f.LocalPath())
1333 if filename == 'DEPS':
1334 virtual_depended_on_files.update(
1335 _CalculateAddedDeps(input_api.os_path,
1336 '\n'.join(f.OldContents()),
1337 '\n'.join(f.NewContents())))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001338
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001339 if not virtual_depended_on_files:
1340 return []
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001341
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001342 if input_api.is_committing:
1343 if input_api.tbr:
1344 return [
1345 output_api.PresubmitNotifyResult(
1346 '--tbr was specified, skipping OWNERS check for DEPS additions'
1347 )
1348 ]
1349 if input_api.dry_run:
1350 return [
1351 output_api.PresubmitNotifyResult(
1352 'This is a dry run, skipping OWNERS check for DEPS additions'
1353 )
1354 ]
1355 if not input_api.change.issue:
1356 return [
1357 output_api.PresubmitError(
1358 "DEPS approval by OWNERS check failed: this change has "
1359 "no change number, so we can't check it for approvals.")
1360 ]
1361 output = output_api.PresubmitError
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001362 else:
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001363 output = output_api.PresubmitNotifyResult
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001364
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001365 owner_email, reviewers = (
1366 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1367 input_api,
Edward Lesmesbef08502021-02-05 14:08:32 -08001368 None,
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001369 approval_needed=input_api.is_committing))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001370
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001371 owner_email = owner_email or input_api.change.author_email
1372
Edward Lesmesbef08502021-02-05 14:08:32 -08001373 approval_status = input_api.owners_client.GetFilesApprovalStatus(
1374 virtual_depended_on_files, reviewers.union([owner_email]), [])
1375 missing_files = [
1376 f for f in virtual_depended_on_files
1377 if approval_status[f] != input_api.owners_client.APPROVED]
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001378
1379 # We strip the /DEPS part that was added by
1380 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1381 # directory.
1382 def StripDeps(path):
1383 start_deps = path.rfind('/DEPS')
1384 if start_deps != -1:
1385 return path[:start_deps]
1386 else:
1387 return path
1388
1389 unapproved_dependencies = [
1390 "'+%s'," % StripDeps(path) for path in missing_files
1391 ]
1392
1393 if unapproved_dependencies:
1394 output_list = [
1395 output(
1396 'You need LGTM from owners of depends-on paths in DEPS that were '
1397 'modified in this CL:\n %s' %
1398 '\n '.join(sorted(unapproved_dependencies)))
1399 ]
Edward Lesmesbef08502021-02-05 14:08:32 -08001400 suggested_owners = input_api.owners_client.SuggestOwners(
1401 missing_files, exclude=[owner_email])
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001402 output_list.append(
1403 output('Suggested missing target path OWNERS:\n %s' %
1404 '\n '.join(suggested_owners or [])))
1405 return output_list
1406
1407 return []