blob: 21875f61afd8a141e3868505565b6b3b0da976bf [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(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900149 'Directories to native API headers have changed which has made '
150 'the list in PRESUBMIT.py outdated.\nPlease update it to the '
151 'current location of our native APIs.', non_existing_paths)
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100152 ]
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(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900215 'Do not #include <iostream> in header files, since it inserts '
216 'static initialization into every file including the header. '
217 'Instead, #include <ostream>. See http://crbug.com/94794',
218 files)
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100219 ]
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'
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900240 'See http://www.chromium.org/developers/coding-style'
241 '#TOC-File-headers',
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100242 files)
243 ]
244 return []
kjellander6aeef742017-02-20 01:13:18 -0800245
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900246def CheckNoFRIEND_TEST(# pylint: disable=invalid-name
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100247 input_api,
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900248 output_api,
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100249 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(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900266 'WebRTC\'s code should not use gtest\'s FRIEND_TEST() macro. '
267 'Include testsupport/gtest_prod_util.h and use '
268 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100269 ]
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(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900349 'Referencing source files above the directory of the GN file '
350 'is not allowed. Please introduce new GN targets in the proper '
351 'location instead.\n'
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100352 '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
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900410 # (c_files, cc_files, objc_files) that keeps track of all the
411 # sources defined in a target. A GN target can have more that
412 # on definition of sources (since it supports if/else statements).
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100413 # 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'
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900457 'Please create a separate target for each collection of '
458 'sources.\n'
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100459 'Mixed sources: \n'
460 '%s\n'
461 'Violating GN files:\n%s\n' %
462 (json.dumps(errors, indent=2), '\n'.join(errors.keys())))
463 ]
464 return []
kjellander7439f972016-12-05 22:47:46 -0800465
Artem Titove92675b2018-05-22 10:21:27 +0200466
charujain9893e252017-09-14 13:33:22 +0200467def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100468 cwd = input_api.PresubmitLocalPath()
469 with _AddToPath(
470 input_api.os_path.join(cwd, 'tools_webrtc',
471 'presubmit_checks_lib')):
472 from check_package_boundaries import CheckPackageBoundaries
473 build_files = [
474 os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files
475 ]
476 errors = CheckPackageBoundaries(cwd, build_files)[:5]
477 if errors:
478 return [
479 output_api.PresubmitError(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900480 'There are package boundary violations in the following GN '
481 'files:', long_text='\n\n'.join(str(err) for err in errors))
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100482 ]
483 return []
ehmaldonado4fb97462017-01-30 05:27:22 -0800484
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100485
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200486def _ReportFileAndLine(filename, line_num):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100487 """Default error formatter for _FindNewViolationsOfRule."""
488 return '%s (line %s)' % (filename, line_num)
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100489
490
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100491def CheckNoWarningSuppressionFlagsAreAdded(gn_files,
492 input_api,
493 output_api,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200494 error_formatter=_ReportFileAndLine):
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900495 """Ensure warning suppression flags are not added wihtout a reason."""
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100496 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
497 'in WebRTC.\n'
498 'If you are not adding this code (e.g. you are just moving '
499 'existing code) or you want to add an exception,\n'
500 'you can add a comment on the line that causes the problem:\n\n'
501 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
502 '\n'
503 'Affected files:\n')
504 errors = [] # 2-element tuples with (file, line number)
505 clang_warn_re = input_api.re.compile(
506 r'//build/config/clang:extra_warnings')
507 no_presubmit_re = input_api.re.compile(
508 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
509 for f in gn_files:
510 for line_num, line in f.ChangedContents():
511 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
512 errors.append(error_formatter(f.LocalPath(), line_num))
513 if errors:
514 return [output_api.PresubmitError(msg, errors)]
515 return []
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200516
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100517
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100518def CheckNoTestCaseUsageIsAdded(input_api,
519 output_api,
520 source_file_filter,
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100521 error_formatter=_ReportFileAndLine):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100522 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
523 'new API: https://github.com/google/googletest/blob/master/'
524 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
525 'Affected files:\n')
526 errors = [] # 2-element tuples with (file, line number)
527 test_case_re = input_api.re.compile(r'TEST_CASE')
528 file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith(
529 '.cc'))
530 for f in input_api.AffectedSourceFiles(file_filter):
531 for line_num, line in f.ChangedContents():
532 if test_case_re.search(line):
533 errors.append(error_formatter(f.LocalPath(), line_num))
534 if errors:
535 return [output_api.PresubmitError(error_msg, errors)]
536 return []
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100537
538
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100539def CheckNoStreamUsageIsAdded(input_api,
540 output_api,
Artem Titov739351d2018-05-11 12:21:36 +0200541 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200542 error_formatter=_ReportFileAndLine):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100543 """Make sure that no more dependencies on stringstream are added."""
544 error_msg = (
545 'Usage of <sstream>, <istream> and <ostream> in WebRTC is '
546 'deprecated.\n'
547 'This includes the following types:\n'
548 'std::istringstream, std::ostringstream, std::wistringstream, '
549 'std::wostringstream,\n'
550 'std::wstringstream, std::ostream, std::wostream, std::istream,'
551 'std::wistream,\n'
552 'std::iostream, std::wiostream.\n'
553 'If you are not adding this code (e.g. you are just moving '
554 'existing code),\n'
555 'you can add a comment on the line that causes the problem:\n\n'
556 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
557 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
558 '\n'
559 'If you are adding new code, consider using '
560 'rtc::SimpleStringBuilder\n'
561 '(in rtc_base/strings/string_builder.h).\n'
562 'Affected files:\n')
563 errors = [] # 2-element tuples with (file, line number)
564 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
565 usage_re = input_api.re.compile(
566 r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
567 no_presubmit_re = input_api.re.compile(
568 r'// no-presubmit-check TODO\(webrtc:8982\)')
569 file_filter = lambda x: (input_api.FilterSourceFile(x) and
570 source_file_filter(x))
Mirko Bonadei571791a2019-05-07 14:08:05 +0200571
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100572 def _IsException(file_path):
573 is_test = any(
574 file_path.endswith(x) for x in
575 ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc'])
576 return (file_path.startswith('examples')
577 or file_path.startswith('test') or is_test)
Patrik Höglund2ea27962020-01-13 15:10:40 +0100578
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100579 for f in input_api.AffectedSourceFiles(file_filter):
580 # Usage of stringstream is allowed under examples/ and in tests.
581 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
582 continue
583 for line_num, line in f.ChangedContents():
584 if ((include_re.search(line) or usage_re.search(line))
585 and not no_presubmit_re.search(line)):
586 errors.append(error_formatter(f.LocalPath(), line_num))
587 if errors:
588 return [output_api.PresubmitError(error_msg, errors)]
589 return []
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100590
Artem Titove92675b2018-05-22 10:21:27 +0200591
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200592def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100593 """Checks that public_deps is not used without a good reason."""
594 result = []
595 no_presubmit_check_re = input_api.re.compile(
596 r'# no-presubmit-check TODO\(webrtc:\d+\)')
597 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
598 'because it doesn\'t map well to downstream build systems.\n'
599 'Used in: %s (line %d).\n'
600 'If you are not adding this code (e.g. you are just moving '
601 'existing code) or you have a good reason, you can add this '
602 'comment (verbatim) on the line that causes the problem:\n\n'
603 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
604 for affected_file in gn_files:
605 for (line_number, affected_line) in affected_file.ChangedContents():
606 if 'public_deps' in affected_line:
607 surpressed = no_presubmit_check_re.search(affected_line)
608 if not surpressed:
609 result.append(
610 output_api.PresubmitError(
611 error_msg %
612 (affected_file.LocalPath(), line_number)))
613 return result
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100614
Artem Titove92675b2018-05-22 10:21:27 +0200615
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700616def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100617 result = []
618 error_msg = (
619 'check_includes overrides are not allowed since it can cause '
620 'incorrect dependencies to form. It effectively means that your '
621 'module can include any .h file without depending on its '
622 'corresponding target. There are some exceptional cases when '
623 'this is allowed: if so, get approval from a .gn owner in the '
624 'root OWNERS file.\n'
625 'Used in: %s (line %d).')
626 no_presubmit_re = input_api.re.compile(
627 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
628 for affected_file in gn_files:
629 for (line_number, affected_line) in affected_file.ChangedContents():
630 if ('check_includes' in affected_line
631 and not no_presubmit_re.search(affected_line)):
632 result.append(
633 output_api.PresubmitError(
634 error_msg % (affected_file.LocalPath(), line_number)))
635 return result
Patrik Höglund6f491062018-01-11 12:04:23 +0100636
Artem Titove92675b2018-05-22 10:21:27 +0200637
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200638def CheckGnChanges(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100639 file_filter = lambda x: (input_api.FilterSourceFile(
640 x,
641 files_to_check=(r'.+\.(gn|gni)$', ),
642 files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', )))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700643
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100644 gn_files = []
645 for f in input_api.AffectedSourceFiles(file_filter):
646 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700647
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100648 result = []
649 if gn_files:
650 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
651 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
652 result.extend(CheckAbseilDependencies(input_api, gn_files, output_api))
653 result.extend(
654 CheckNoPackageBoundaryViolations(input_api, gn_files, output_api))
655 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api,
656 output_api))
657 result.extend(
658 CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
659 result.extend(
660 CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200661 output_api))
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100662 return result
ehmaldonado5b1ba082016-09-02 05:51:08 -0700663
Artem Titove92675b2018-05-22 10:21:27 +0200664
Oleh Prypin920b6532017-10-05 11:28:51 +0200665def CheckGnGen(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100666 """Runs `gn gen --check` with default args to detect mismatches between
Oleh Prypin920b6532017-10-05 11:28:51 +0200667 #includes and dependencies in the BUILD.gn files, as well as general build
668 errors.
669 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100670 with _AddToPath(
671 input_api.os_path.join(input_api.PresubmitLocalPath(),
672 'tools_webrtc', 'presubmit_checks_lib')):
673 from build_helpers import RunGnCheck
674 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
675 if errors:
676 return [
677 output_api.PresubmitPromptWarning(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900678 'Some #includes do not match the build dependency graph. '
679 'Please run:\n'
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100680 ' gn gen --check <out_dir>',
681 long_text='\n\n'.join(errors))
682 ]
683 return []
Oleh Prypin920b6532017-10-05 11:28:51 +0200684
Artem Titove92675b2018-05-22 10:21:27 +0200685
Artem Titova04d1402018-05-11 11:23:00 +0200686def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100687 """Runs checkdeps on #include statements added in this
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000688 change. Breaking - rules is an error, breaking ! rules is a
689 warning.
690 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100691 # Copied from Chromium's src/PRESUBMIT.py.
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000692
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100693 # We need to wait until we have an input_api object and use this
694 # roundabout construct to import checkdeps because this file is
695 # eval-ed and thus doesn't have __file__.
696 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
697 checkdeps_path = input_api.os_path.join(src_path, 'buildtools',
698 'checkdeps')
699 if not os.path.exists(checkdeps_path):
700 return [
701 output_api.PresubmitError(
702 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
703 'download all the DEPS entries?' % checkdeps_path)
704 ]
705 with _AddToPath(checkdeps_path):
706 import checkdeps
707 from cpp_checker import CppChecker
708 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000709
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100710 added_includes = []
711 for f in input_api.AffectedFiles(file_filter=source_file_filter):
712 if not CppChecker.IsCppFile(f.LocalPath()):
713 continue
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000714
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100715 changed_lines = [line for _, line in f.ChangedContents()]
716 added_includes.append([f.LocalPath(), changed_lines])
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000717
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100718 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000719
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100720 error_descriptions = []
721 warning_descriptions = []
722 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
723 added_includes):
724 description_with_path = '%s\n %s' % (path, rule_description)
725 if rule_type == Rule.DISALLOW:
726 error_descriptions.append(description_with_path)
727 else:
728 warning_descriptions.append(description_with_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000729
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100730 results = []
731 if error_descriptions:
732 results.append(
733 output_api.PresubmitError(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900734 'You added one or more #includes that violate checkdeps rules.'
735 '\nCheck that the DEPS files in these locations contain valid '
736 'rules.\nSee '
737 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
738 'for more details about checkdeps.', error_descriptions))
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100739 if warning_descriptions:
740 results.append(
741 output_api.PresubmitPromptOrNotify(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900742 'You added one or more #includes of files that are temporarily'
743 '\nallowed but being removed. Can you avoid introducing the\n'
744 '#include? See relevant DEPS file(s) for details and contacts.'
745 '\nSee '
746 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
747 'for more details about checkdeps.', warning_descriptions))
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100748 return results
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000749
Artem Titove92675b2018-05-22 10:21:27 +0200750
charujain9893e252017-09-14 13:33:22 +0200751def CheckCommitMessageBugEntry(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100752 """Check that bug entries are well-formed in commit message."""
753 bogus_bug_msg = (
754 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
755 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.'
756 )
757 results = []
758 for bug in input_api.change.BugsFromDescription():
759 bug = bug.strip()
760 if bug.lower() == 'none':
761 continue
762 if 'b/' not in bug and ':' not in bug:
763 try:
764 if int(bug) > 100000:
765 # Rough indicator for current chromium bugs.
766 prefix_guess = 'chromium'
767 else:
768 prefix_guess = 'webrtc'
769 results.append(
770 'Bug entry requires issue tracker prefix, e.g. %s:%s' %
771 (prefix_guess, bug))
772 except ValueError:
773 results.append(bogus_bug_msg % bug)
774 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
775 results.append(bogus_bug_msg % bug)
776 return [output_api.PresubmitError(r) for r in results]
charujain9893e252017-09-14 13:33:22 +0200777
Artem Titove92675b2018-05-22 10:21:27 +0200778
charujain9893e252017-09-14 13:33:22 +0200779def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100780 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700781
782 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200783 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700784 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200785
786 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
787 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700788 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100789 if input_api.change.BugsFromDescription():
790 return []
791 else:
792 return [
793 output_api.PresubmitError(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900794 'The "Bug: [bug number]" footer is mandatory. Please create a '
795 'bug and reference it using either of:\n'
796 ' * https://bugs.webrtc.org - reference it using Bug: '
797 'webrtc:XXXX\n'
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100798 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX'
799 )
800 ]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000801
Artem Titove92675b2018-05-22 10:21:27 +0200802
Artem Titova04d1402018-05-11 11:23:00 +0200803def CheckJSONParseErrors(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100804 """Check that JSON files do not contain syntax errors."""
kjellander569cf942016-02-11 05:02:59 -0800805
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100806 def FilterFile(affected_file):
807 return (input_api.os_path.splitext(
808 affected_file.LocalPath())[1] == '.json'
809 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800810
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100811 def GetJSONParseError(input_api, filename):
812 try:
813 contents = input_api.ReadFile(filename)
814 input_api.json.loads(contents)
815 except ValueError as e:
816 return e
817 return None
kjellander569cf942016-02-11 05:02:59 -0800818
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100819 results = []
820 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
821 include_deletes=False):
822 parse_error = GetJSONParseError(input_api,
823 affected_file.AbsoluteLocalPath())
824 if parse_error:
825 results.append(
826 output_api.PresubmitError(
827 '%s could not be parsed: %s' %
828 (affected_file.LocalPath(), parse_error)))
829 return results
kjellander569cf942016-02-11 05:02:59 -0800830
831
charujain9893e252017-09-14 13:33:22 +0200832def RunPythonTests(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100833 def Join(*args):
834 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200835
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100836 test_directories = [
837 input_api.PresubmitLocalPath(),
838 Join('rtc_tools', 'py_event_log_analyzer'),
839 Join('audio', 'test', 'unittests'),
840 ] + [
841 root for root, _, files in os.walk(Join('tools_webrtc')) if any(
842 f.endswith('_test.py') for f in files)
843 ]
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200844
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100845 tests = []
846 for directory in test_directories:
847 tests.extend(
848 input_api.canned_checks.GetUnitTestsInDirectory(
849 input_api,
850 output_api,
851 directory,
852 files_to_check=[r'.+_test\.py$']))
853 return input_api.RunTests(tests, parallel=True)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200854
855
Artem Titova04d1402018-05-11 11:23:00 +0200856def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
857 source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100858 """Checks that the namespace google::protobuf has not been used."""
859 files = []
860 pattern = input_api.re.compile(r'google::protobuf')
861 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
862 file_filter = lambda x: (input_api.FilterSourceFile(x) and
863 source_file_filter(x))
864 for f in input_api.AffectedSourceFiles(file_filter):
865 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
866 continue
867 contents = input_api.ReadFile(f)
868 if pattern.search(contents):
869 files.append(f)
mbonadei38415b22017-04-07 05:38:01 -0700870
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100871 if files:
872 return [
873 output_api.PresubmitError(
874 'Please avoid to use namespace `google::protobuf` directly.\n'
875 'Add a using directive in `%s` and include that header instead.'
876 % proto_utils_path, files)
877 ]
878 return []
mbonadei38415b22017-04-07 05:38:01 -0700879
880
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200881def _LicenseHeader(input_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100882 """Returns the license header regexp."""
883 # Accept any year number from 2003 to the current year
884 current_year = int(input_api.time.strftime('%Y'))
885 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
886 years_re = '(' + '|'.join(allowed_years) + ')'
887 license_header = (
888 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200889 r'All [Rr]ights [Rr]eserved\.\n'
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100890 r'.*?\n'
891 r'.*? Use of this source code is governed by a BSD-style license\n'
892 r'.*? that can be found in the LICENSE file in the root of the source\n'
893 r'.*? tree\. An additional intellectual property rights grant can be '
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200894 r'found\n'
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100895 r'.*? in the file PATENTS\. All contributing project authors may\n'
896 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
897 ) % {
898 'year': years_re,
899 }
900 return license_header
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200901
902
charujain9893e252017-09-14 13:33:22 +0200903def CommonChecks(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100904 """Checks common to both upload and commit."""
905 results = []
906 # Filter out files that are in objc or ios dirs from being cpplint-ed since
907 # they do not follow C++ lint rules.
908 exception_list = input_api.DEFAULT_FILES_TO_SKIP + (
909 r".*\bobjc[\\\/].*",
910 r".*objc\.[hcm]+$",
911 )
912 source_file_filter = lambda x: input_api.FilterSourceFile(
913 x, None, exception_list)
914 results.extend(
915 CheckApprovedFilesLintClean(input_api, output_api, source_file_filter))
916 results.extend(
917 input_api.canned_checks.CheckLicense(input_api, output_api,
918 _LicenseHeader(input_api)))
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900919
920 # TODO(bugs.webrtc.org/12114): Delete this filter and run pylint on
921 # all python files. This is a temporary solution.
922 python_file_filter = lambda f: (f.LocalPath().endswith('.py') and
923 source_file_filter(f))
924 python_changed_files = [f.LocalPath() for f in input_api.AffectedFiles(
925 file_filter=python_file_filter)]
926
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100927 results.extend(
928 input_api.canned_checks.RunPylint(
929 input_api,
930 output_api,
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900931 files_to_check=python_changed_files,
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100932 files_to_skip=(
933 r'^base[\\\/].*\.py$',
934 r'^build[\\\/].*\.py$',
935 r'^buildtools[\\\/].*\.py$',
936 r'^infra[\\\/].*\.py$',
937 r'^ios[\\\/].*\.py$',
938 r'^out.*[\\\/].*\.py$',
939 r'^testing[\\\/].*\.py$',
940 r'^third_party[\\\/].*\.py$',
941 r'^tools[\\\/].*\.py$',
942 # TODO(phoglund): should arguably be checked.
943 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
944 r'^xcodebuild.*[\\\/].*\.py$',
945 ),
946 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800947
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100948 # TODO(nisse): talk/ is no more, so make below checks simpler?
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900949 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function
950 # since we need to have different license checks
951 # in talk/ and webrtc/directories.
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100952 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200953
Byoungchan Lee94f2ef22021-07-01 22:21:44 +0900954 # .m and .mm files are ObjC files. For simplicity we will consider
955 # .h files in ObjC subdirectories ObjC headers.
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100956 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
957 # Skip long-lines check for DEPS and GN files.
958 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
959 # Also we will skip most checks for third_party directory.
960 third_party_filter_list = (r'^third_party[\\\/].+', )
961 eighty_char_sources = lambda x: input_api.FilterSourceFile(
962 x,
963 files_to_skip=build_file_filter_list + objc_filter_list +
964 third_party_filter_list)
965 hundred_char_sources = lambda x: input_api.FilterSourceFile(
966 x, files_to_check=objc_filter_list)
967 non_third_party_sources = lambda x: input_api.FilterSourceFile(
968 x, files_to_skip=third_party_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200969
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100970 results.extend(
971 input_api.canned_checks.CheckLongLines(
972 input_api,
973 output_api,
974 maxlen=80,
975 source_file_filter=eighty_char_sources))
976 results.extend(
977 input_api.canned_checks.CheckLongLines(
978 input_api,
979 output_api,
980 maxlen=100,
981 source_file_filter=hundred_char_sources))
982 results.extend(
983 input_api.canned_checks.CheckChangeHasNoTabs(
984 input_api, output_api, source_file_filter=non_third_party_sources))
985 results.extend(
986 input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
987 input_api, output_api, source_file_filter=non_third_party_sources))
988 results.extend(
989 input_api.canned_checks.CheckAuthorizedAuthor(
990 input_api,
991 output_api,
992 bot_allowlist=[
Mirko Bonadei39423802020-12-15 12:02:26 +0100993 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com',
994 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com',
Mirko Bonadei8cc66952020-10-30 10:13:45 +0100995 ]))
996 results.extend(
997 input_api.canned_checks.CheckChangeTodoHasOwner(
998 input_api, output_api, source_file_filter=non_third_party_sources))
999 results.extend(
1000 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
1001 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
1002 results.extend(
1003 CheckNoIOStreamInHeaders(input_api,
1004 output_api,
1005 source_file_filter=non_third_party_sources))
1006 results.extend(
1007 CheckNoPragmaOnce(input_api,
1008 output_api,
1009 source_file_filter=non_third_party_sources))
1010 results.extend(
1011 CheckNoFRIEND_TEST(input_api,
1012 output_api,
1013 source_file_filter=non_third_party_sources))
1014 results.extend(CheckGnChanges(input_api, output_api))
1015 results.extend(
1016 CheckUnwantedDependencies(input_api,
1017 output_api,
1018 source_file_filter=non_third_party_sources))
1019 results.extend(
1020 CheckJSONParseErrors(input_api,
1021 output_api,
1022 source_file_filter=non_third_party_sources))
1023 results.extend(RunPythonTests(input_api, output_api))
1024 results.extend(
1025 CheckUsageOfGoogleProtobufNamespace(
1026 input_api, output_api, source_file_filter=non_third_party_sources))
1027 results.extend(
1028 CheckOrphanHeaders(input_api,
1029 output_api,
1030 source_file_filter=non_third_party_sources))
1031 results.extend(
1032 CheckNewlineAtTheEndOfProtoFiles(
1033 input_api, output_api, source_file_filter=non_third_party_sources))
1034 results.extend(
1035 CheckNoStreamUsageIsAdded(input_api, output_api,
1036 non_third_party_sources))
1037 results.extend(
1038 CheckNoTestCaseUsageIsAdded(input_api, output_api,
1039 non_third_party_sources))
1040 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1041 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
1042 results.extend(
1043 CheckAbslMemoryInclude(input_api, output_api, non_third_party_sources))
1044 results.extend(
1045 CheckBannedAbslMakeUnique(input_api, output_api,
1046 non_third_party_sources))
1047 results.extend(
1048 CheckObjcApiSymbols(input_api, output_api, non_third_party_sources))
1049 return results
Mirko Bonadeia418e672018-10-24 13:57:25 +02001050
1051
1052def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001053 """Check that 'include_rules' in api/DEPS is up to date.
Mirko Bonadei90490372018-10-26 13:17:47 +02001054
1055 The file api/DEPS must be kept up to date in order to avoid to avoid to
1056 include internal header from WebRTC's api/ headers.
1057
1058 This check is focused on ensuring that 'include_rules' contains a deny
1059 rule for each root level directory. More focused allow rules can be
1060 added to 'specific_include_rules'.
1061 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001062 results = []
1063 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
1064 with open(api_deps) as f:
1065 deps_content = _ParseDeps(f.read())
Mirko Bonadeia418e672018-10-24 13:57:25 +02001066
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001067 include_rules = deps_content.get('include_rules', [])
1068 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 13:57:25 +02001069
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001070 # Only check top level directories affected by the current CL.
1071 dirs_to_check = set()
1072 for f in input_api.AffectedFiles():
1073 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
1074 if len(path_tokens) > 1:
1075 if (path_tokens[0] not in dirs_to_skip and os.path.isdir(
1076 os.path.join(input_api.PresubmitLocalPath(),
1077 path_tokens[0]))):
1078 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 13:57:25 +02001079
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001080 missing_include_rules = set()
1081 for p in dirs_to_check:
1082 rule = '-%s' % p
1083 if rule not in include_rules:
1084 missing_include_rules.add(rule)
Mirko Bonadei90490372018-10-26 13:17:47 +02001085
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001086 if missing_include_rules:
1087 error_msg = [
1088 'include_rules = [\n',
1089 ' ...\n',
1090 ]
Mirko Bonadeia418e672018-10-24 13:57:25 +02001091
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001092 for r in sorted(missing_include_rules):
1093 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 13:57:25 +02001094
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001095 error_msg.append(' ...\n')
1096 error_msg.append(']\n')
Mirko Bonadei90490372018-10-26 13:17:47 +02001097
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001098 results.append(
1099 output_api.PresubmitError(
1100 'New root level directory detected! WebRTC api/ headers should '
1101 'not #include headers from \n'
1102 'the new directory, so please update "include_rules" in file\n'
1103 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
Mirko Bonadei90490372018-10-26 13:17:47 +02001104
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001105 return results
1106
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001107
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001108def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001109 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
1110 source_file_filter(f))
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001111
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001112 files = []
1113 for f in input_api.AffectedFiles(include_deletes=False,
1114 file_filter=file_filter):
1115 for _, line in f.ChangedContents():
1116 if 'absl::make_unique' in line:
1117 files.append(f)
1118 break
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001119
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001120 if len(files):
1121 return [
1122 output_api.PresubmitError(
1123 'Please use std::make_unique instead of absl::make_unique.\n'
1124 'Affected files:', files)
1125 ]
1126 return []
1127
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001128
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001129def CheckObjcApiSymbols(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001130 rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}',
1131 re.MULTILINE | re.DOTALL)
1132 file_filter = lambda f: (f.LocalPath().endswith(('.h')) and
1133 source_file_filter(f))
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001134
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001135 files = []
1136 file_filter = lambda x: (input_api.FilterSourceFile(x) and
1137 source_file_filter(x))
1138 for f in input_api.AffectedSourceFiles(file_filter):
1139 if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath():
1140 continue
Niels Möller11ab77d2020-11-16 15:38:23 +01001141 if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'):
1142 continue
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001143 contents = input_api.ReadFile(f)
1144 for match in rtc_objc_export.finditer(contents):
1145 export_block = match.group(0)
1146 if 'RTC_OBJC_TYPE' not in export_block:
1147 files.append(f.LocalPath())
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001148
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001149 if len(files):
1150 return [
1151 output_api.PresubmitError(
1152 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() '
1153 + 'macro.\n\n' + 'For example:\n' +
1154 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' +
1155 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' +
1156 'Please fix the following files:', files)
1157 ]
1158 return []
1159
Mirko Bonadeid74c0e62020-07-16 21:57:01 +02001160
tzika06bf852018-11-15 20:37:35 +09001161def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001162 pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"',
1163 input_api.re.MULTILINE)
1164 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
1165 source_file_filter(f))
tzika06bf852018-11-15 20:37:35 +09001166
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001167 files = []
1168 for f in input_api.AffectedFiles(include_deletes=False,
1169 file_filter=file_filter):
1170 contents = input_api.ReadFile(f)
1171 if pattern.search(contents):
1172 continue
1173 for _, line in f.ChangedContents():
1174 if 'absl::WrapUnique' in line:
1175 files.append(f)
1176 break
tzika06bf852018-11-15 20:37:35 +09001177
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001178 if len(files):
1179 return [
1180 output_api.PresubmitError(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +09001181 'Please include "absl/memory/memory.h" header for '
1182 'absl::WrapUnique.\nThis header may or may not be included '
1183 'transitively depending on the C++ standard version.', files)
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001184 ]
1185 return []
1186
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001187
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001188def CheckChangeOnUpload(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001189 results = []
1190 results.extend(CommonChecks(input_api, output_api))
1191 results.extend(CheckGnGen(input_api, output_api))
1192 results.extend(
1193 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
1194 return results
niklase@google.comda159d62011-05-30 11:51:34 +00001195
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001196
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001197def CheckChangeOnCommit(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001198 results = []
1199 results.extend(CommonChecks(input_api, output_api))
1200 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
1201 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
1202 results.extend(
1203 input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api))
1204 results.extend(
1205 input_api.canned_checks.CheckChangeHasDescription(
1206 input_api, output_api))
1207 results.extend(CheckChangeHasBugField(input_api, output_api))
1208 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
1209 results.extend(
1210 input_api.canned_checks.CheckTreeIsOpen(
1211 input_api,
1212 output_api,
1213 json_url='http://webrtc-status.appspot.com/current?format=json'))
1214 return results
mbonadei74973ed2017-05-09 07:58:05 -07001215
1216
Artem Titova04d1402018-05-11 11:23:00 +02001217def CheckOrphanHeaders(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001218 # We need to wait until we have an input_api object and use this
1219 # roundabout construct to import prebubmit_checks_lib because this file is
1220 # eval-ed and thus doesn't have __file__.
1221 error_msg = """{} should be listed in {}."""
1222 results = []
1223 exempt_paths = [
1224 os.path.join('tools_webrtc', 'ios', 'SDK'),
1225 ]
1226 with _AddToPath(
1227 input_api.os_path.join(input_api.PresubmitLocalPath(),
1228 'tools_webrtc', 'presubmit_checks_lib')):
1229 from check_orphan_headers import GetBuildGnPathFromFilePath
1230 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -07001231
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001232 file_filter = lambda x: input_api.FilterSourceFile(
1233 x, files_to_skip=exempt_paths) and source_file_filter(x)
1234 for f in input_api.AffectedSourceFiles(file_filter):
1235 if f.LocalPath().endswith('.h'):
1236 file_path = os.path.abspath(f.LocalPath())
1237 root_dir = os.getcwd()
1238 gn_file_path = GetBuildGnPathFromFilePath(file_path,
1239 os.path.exists, root_dir)
1240 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1241 if not in_build_gn:
1242 results.append(
1243 output_api.PresubmitError(
1244 error_msg.format(f.LocalPath(),
1245 os.path.relpath(gn_file_path))))
1246 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001247
1248
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001249def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api,
1250 source_file_filter):
1251 """Checks that all .proto files are terminated with a newline."""
1252 error_msg = 'File {} must end with exactly one newline.'
1253 results = []
1254 file_filter = lambda x: input_api.FilterSourceFile(
1255 x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x)
1256 for f in input_api.AffectedSourceFiles(file_filter):
1257 file_path = f.LocalPath()
1258 with open(file_path) as f:
1259 lines = f.readlines()
1260 if len(lines) > 0 and not lines[-1].endswith('\n'):
1261 results.append(
1262 output_api.PresubmitError(error_msg.format(file_path)))
1263 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001264
1265
1266def _ExtractAddRulesFromParsedDeps(parsed_deps):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001267 """Extract the rules that add dependencies from a parsed DEPS file.
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001268
1269 Args:
1270 parsed_deps: the locals dictionary from evaluating the DEPS file."""
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001271 add_rules = set()
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001272 add_rules.update([
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001273 rule[1:] for rule in parsed_deps.get('include_rules', [])
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001274 if rule.startswith('+') or rule.startswith('!')
1275 ])
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001276 for _, rules in parsed_deps.get('specific_include_rules', {}).iteritems():
1277 add_rules.update([
1278 rule[1:] for rule in rules
1279 if rule.startswith('+') or rule.startswith('!')
1280 ])
1281 return add_rules
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001282
1283
1284def _ParseDeps(contents):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001285 """Simple helper for parsing DEPS files."""
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001286
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001287 # Stubs for handling special syntax in the root DEPS file.
1288 class VarImpl(object):
1289 def __init__(self, local_scope):
1290 self._local_scope = local_scope
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001291
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001292 def Lookup(self, var_name):
1293 """Implements the Var syntax."""
1294 try:
1295 return self._local_scope['vars'][var_name]
1296 except KeyError:
1297 raise Exception('Var is not defined: %s' % var_name)
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001298
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001299 local_scope = {}
1300 global_scope = {
1301 'Var': VarImpl(local_scope).Lookup,
1302 }
1303 exec contents in global_scope, local_scope
1304 return local_scope
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001305
1306
1307def _CalculateAddedDeps(os_path, old_contents, new_contents):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001308 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001309 a set of DEPS entries that we should look up.
1310
1311 For a directory (rather than a specific filename) we fake a path to
1312 a specific filename by adding /DEPS. This is chosen as a file that
1313 will seldom or never be subject to per-file include_rules.
1314 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001315 # We ignore deps entries on auto-generated directories.
1316 auto_generated_dirs = ['grit', 'jni']
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001317
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001318 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1319 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001320
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001321 added_deps = new_deps.difference(old_deps)
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001322
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001323 results = set()
1324 for added_dep in added_deps:
1325 if added_dep.split('/')[0] in auto_generated_dirs:
1326 continue
1327 # Assume that a rule that ends in .h is a rule for a specific file.
1328 if added_dep.endswith('.h'):
1329 results.add(added_dep)
1330 else:
1331 results.add(os_path.join(added_dep, 'DEPS'))
1332 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001333
1334
1335def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001336 """When a dependency prefixed with + is added to a DEPS file, we
Edward Lesmesbef08502021-02-05 14:08:32 -08001337 want to make sure that the change is reviewed by an OWNER of the
1338 target file or directory, to avoid layering violations from being
1339 introduced. This check verifies that this happens.
1340 """
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001341 virtual_depended_on_files = set()
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001342
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001343 file_filter = lambda f: not input_api.re.match(
1344 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1345 for f in input_api.AffectedFiles(include_deletes=False,
1346 file_filter=file_filter):
1347 filename = input_api.os_path.basename(f.LocalPath())
1348 if filename == 'DEPS':
1349 virtual_depended_on_files.update(
1350 _CalculateAddedDeps(input_api.os_path,
1351 '\n'.join(f.OldContents()),
1352 '\n'.join(f.NewContents())))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001353
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001354 if not virtual_depended_on_files:
1355 return []
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001356
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001357 if input_api.is_committing:
1358 if input_api.tbr:
1359 return [
1360 output_api.PresubmitNotifyResult(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +09001361 '--tbr was specified, skipping OWNERS check for DEPS '
1362 'additions'
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001363 )
1364 ]
1365 if input_api.dry_run:
1366 return [
1367 output_api.PresubmitNotifyResult(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +09001368 'This is a dry run, skipping OWNERS check for DEPS '
1369 'additions'
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001370 )
1371 ]
1372 if not input_api.change.issue:
1373 return [
1374 output_api.PresubmitError(
1375 "DEPS approval by OWNERS check failed: this change has "
1376 "no change number, so we can't check it for approvals.")
1377 ]
1378 output = output_api.PresubmitError
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001379 else:
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001380 output = output_api.PresubmitNotifyResult
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001381
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001382 owner_email, reviewers = (
1383 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1384 input_api,
Edward Lesmesbef08502021-02-05 14:08:32 -08001385 None,
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001386 approval_needed=input_api.is_committing))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001387
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001388 owner_email = owner_email or input_api.change.author_email
1389
Edward Lesmesbef08502021-02-05 14:08:32 -08001390 approval_status = input_api.owners_client.GetFilesApprovalStatus(
1391 virtual_depended_on_files, reviewers.union([owner_email]), [])
1392 missing_files = [
1393 f for f in virtual_depended_on_files
1394 if approval_status[f] != input_api.owners_client.APPROVED]
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001395
1396 # We strip the /DEPS part that was added by
1397 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1398 # directory.
1399 def StripDeps(path):
1400 start_deps = path.rfind('/DEPS')
1401 if start_deps != -1:
1402 return path[:start_deps]
1403 else:
1404 return path
1405
1406 unapproved_dependencies = [
1407 "'+%s'," % StripDeps(path) for path in missing_files
1408 ]
1409
1410 if unapproved_dependencies:
1411 output_list = [
1412 output(
Byoungchan Lee94f2ef22021-07-01 22:21:44 +09001413 'You need LGTM from owners of depends-on paths in DEPS that '
1414 ' were modified in this CL:\n %s' %
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001415 '\n '.join(sorted(unapproved_dependencies)))
1416 ]
Edward Lesmesbef08502021-02-05 14:08:32 -08001417 suggested_owners = input_api.owners_client.SuggestOwners(
1418 missing_files, exclude=[owner_email])
Mirko Bonadei8cc66952020-10-30 10:13:45 +01001419 output_list.append(
1420 output('Suggested missing target path OWNERS:\n %s' %
1421 '\n '.join(suggested_owners or [])))
1422 return output_list
1423
1424 return []