blob: 0591c1bab9bd1731ff97ca6682931d5d589611e7 [file] [log] [blame]
andrew@webrtc.org2442de12012-01-23 17:45:411# 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:348
kjellander7439f972016-12-06 06:47:469import json
kjellander@webrtc.orgaefe61a2014-12-08 13:00:3010import os
kjellander@webrtc.org85759802013-10-22 16:47:4011import re
kjellander@webrtc.org3bd41562014-09-01 11:06:3712import sys
Mirko Bonadei4dc4e252017-09-19 11:49:1613from collections import defaultdict
Oleh Prypin2f33a562017-10-04 18:17:5414from contextlib import contextmanager
kjellander@webrtc.org85759802013-10-22 16:47:4015
oprypin2aa463f2017-03-23 10:17:0216# Files and directories that are *skipped* by cpplint in the presubmit script.
Mirko Bonadeifc17a782020-06-30 12:31:3717CPPLINT_EXCEPTIONS = [
Mirko Bonadei8cc66952020-10-30 09:13:4518 '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 13:06:0924 'media/sctp/usrsctp_transport.cc',
Mirko Bonadei8cc66952020-10-30 09:13:4525 '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 14:24:5241]
42
jbauchc4e3ead2016-02-19 08:25:5543# 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 12:31:3748# include file and feature blocklists are
jbauchc4e3ead2016-02-19 08:25:5549# google3-specific.
Mirko Bonadeie92e2862020-05-29 13:23:0950# - runtime/references : Mutable references are not banned by the Google
51# C++ style guide anymore (starting from May 2020).
kjellandere5a87a52016-04-27 09:32:1252# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
53# all move-related errors).
Mirko Bonadeifc17a782020-06-30 12:31:3754DISABLED_LINT_FILTERS = [
Mirko Bonadei8cc66952020-10-30 09:13:4555 '-build/c++11',
56 '-runtime/references',
57 '-whitespace/operators',
jbauchc4e3ead2016-02-19 08:25:5558]
59
kjellanderfd595232015-12-04 10:44:0960# 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-03 07:56:1467NATIVE_API_DIRS = (
Mirko Bonadei8cc66952020-10-30 09:13:4568 '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 18:17:2773)
Mirko Bonadei4dc4e252017-09-19 11:49:1674
kjellanderdd705472016-06-09 18:17:2775# 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 09:13:4578 '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-03 07:56:1493)
Mirko Bonadei4dc4e252017-09-19 11:49:1694
Karl Wibergd4f01c12017-11-10 09:55:4595# 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 18:17:2797API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-03 07:56:1498
Mirko Bonadei4dc4e252017-09-19 11:49:1699# TARGET_RE matches a GN target, and extracts the target name and the contents.
Mirko Bonadei2dcf3482020-06-05 12:30:41100TARGET_RE = re.compile(
Mirko Bonadei8cc66952020-10-30 09:13:45101 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 11:49:16104
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 12:30:41109# 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
Philipp Hancke0c2a9ca2021-08-11 10:00:27113# FILE_PATH_RE matches a file path.
Mirko Bonadei4dc4e252017-09-19 11:49:16114FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
115
kjellander53047c92015-12-03 07:56:14116
Mirko Bonadeid8665442018-09-04 10:17:27117def FindSrcDirPath(starting_dir):
Mirko Bonadei8cc66952020-10-30 09:13:45118 """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 10:17:27123
124
Oleh Prypin2f33a562017-10-04 18:17:54125@contextmanager
126def _AddToPath(*paths):
Mirko Bonadei8cc66952020-10-30 09:13:45127 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 13:27:22134
135
charujain9893e252017-09-14 11:33:22136def VerifyNativeApiHeadersListIsValid(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45137 """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 13:21:44149 '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 09:13:45152 ]
153 return []
kjellander53047c92015-12-03 07:56:14154
Artem Titove92675b2018-05-22 08:21:27155
kjellanderc88b5d52017-04-05 13:42:43156API_CHANGE_MSG = """
kwibergeb133022016-04-07 14:41:48157You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 12:13:23158 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 13:31:26160 2. Mark the old stuff as deprecated (use the ABSL_DEPRECATED macro).
kwibergeb133022016-04-07 14:41:48161 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-03 07:56:14174
Artem Titove92675b2018-05-22 08:21:27175
charujain9893e252017-09-14 11:33:22176def CheckNativeApiHeaderChanges(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45177 """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-03 07:56:14192
Mirko Bonadei8cc66952020-10-30 09:13:45193 if files:
194 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
195 return []
kjellander53047c92015-12-03 07:56:14196
kjellander@webrtc.org0fcaf992015-11-26 14:24:52197
Mirko Bonadei8cc66952020-10-30 09:13:45198def 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:46211
Mirko Bonadei8cc66952020-10-30 09:13:45212 if len(files):
213 return [
214 output_api.PresubmitError(
Byoungchan Lee94f2ef22021-07-01 13:21:44215 '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 09:13:45219 ]
220 return []
kjellander@webrtc.org51198f12012-02-21 17:53:46221
kjellander@webrtc.orge4158642014-08-06 09:11:18222
Mirko Bonadei8cc66952020-10-30 09:13:45223def 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 09:13:18235
Mirko Bonadei8cc66952020-10-30 09:13:45236 if files:
237 return [
238 output_api.PresubmitError(
239 'Do not use #pragma once in header files.\n'
Byoungchan Lee94f2ef22021-07-01 13:21:44240 'See http://www.chromium.org/developers/coding-style'
241 '#TOC-File-headers',
Mirko Bonadei8cc66952020-10-30 09:13:45242 files)
243 ]
244 return []
kjellander6aeef742017-02-20 09:13:18245
Byoungchan Lee94f2ef22021-07-01 13:21:44246def CheckNoFRIEND_TEST(# pylint: disable=invalid-name
Mirko Bonadei8cc66952020-10-30 09:13:45247 input_api,
Byoungchan Lee94f2ef22021-07-01 13:21:44248 output_api,
Mirko Bonadei8cc66952020-10-30 09:13:45249 source_file_filter):
250 """Make sure that gtest's FRIEND_TEST() macro is not used, the
kjellander@webrtc.org51198f12012-02-21 17:53:46251 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 09:13:45253 problems = []
kjellander@webrtc.org51198f12012-02-21 17:53:46254
Mirko Bonadei8cc66952020-10-30 09:13:45255 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:46261
Mirko Bonadei8cc66952020-10-30 09:13:45262 if not problems:
263 return []
264 return [
265 output_api.PresubmitPromptWarning(
Byoungchan Lee94f2ef22021-07-01 13:21:44266 '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 09:13:45269 ]
kjellander@webrtc.org51198f12012-02-21 17:53:46270
kjellander@webrtc.orge4158642014-08-06 09:11:18271
Mirko Bonadeifc17a782020-06-30 12:31:37272def IsLintDisabled(disabled_paths, file_path):
Mirko Bonadei8cc66952020-10-30 09:13:45273 """ 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 14:24:52278
279
charujain9893e252017-09-14 11:33:22280def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 09:23:00281 source_file_filter=None):
Mirko Bonadei8cc66952020-10-30 09:13:45282 """Checks that all new or non-exempt .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 11:33:22283 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46284 depot_tools/presubmit_canned_checks.py but has less filters and only checks
285 added files."""
Mirko Bonadei8cc66952020-10-30 09:13:45286 result = []
kjellander@webrtc.org51198f12012-02-21 17:53:46287
Mirko Bonadei8cc66952020-10-30 09:13:45288 # 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:46293
Mirko Bonadei8cc66952020-10-30 09:13:45294 lint_filters = cpplint._Filters()
295 lint_filters.extend(DISABLED_LINT_FILTERS)
296 cpplint._SetFilters(','.join(lint_filters))
jbauchc4e3ead2016-02-19 08:25:55297
Mirko Bonadei8cc66952020-10-30 09:13:45298 # 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 14:24:52302
Mirko Bonadei8cc66952020-10-30 09:13:45303 # 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:23314
Mirko Bonadei8cc66952020-10-30 09:13:45315 for file_name in files:
316 cpplint.ProcessFile(file_name, verbosity_level)
kjellander@webrtc.org51198f12012-02-21 17:53:46317
Mirko Bonadei8cc66952020-10-30 09:13:45318 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:46324
Mirko Bonadei8cc66952020-10-30 09:13:45325 return result
kjellander@webrtc.org51198f12012-02-21 17:53:46326
Artem Titove92675b2018-05-22 08:21:27327
charujain9893e252017-09-14 11:33:22328def CheckNoSourcesAbove(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45329 # 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 13:21:44349 '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 09:13:45352 '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 12:51:08358
Artem Titove92675b2018-05-22 08:21:27359
Mirko Bonadei2dcf3482020-06-05 12:30:41360def CheckAbseilDependencies(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45361 """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 12:30:41372
Mirko Bonadei8cc66952020-10-30 09:13:45373 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 12:30:41390
391
Mirko Bonadei4dc4e252017-09-19 11:49:16392def CheckNoMixingSources(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45393 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
Mirko Bonadei4dc4e252017-09-19 11:49:16394
395 See bugs.webrtc.org/7743 for more context.
396 """
Artem Titove92675b2018-05-22 08:21:27397
Mirko Bonadei8cc66952020-10-30 09:13:45398 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 11:49:16404
Mirko Bonadei8cc66952020-10-30 09:13:45405 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 13:21:44410 # (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 09:13:45413 # 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 11:49:16445 list_of_sources.append((c_files, cc_files, objc_files))
Mirko Bonadei8cc66952020-10-30 09:13:45446 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 13:21:44457 'Please create a separate target for each collection of '
458 'sources.\n'
Mirko Bonadei8cc66952020-10-30 09:13:45459 '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-06 06:47:46465
Artem Titove92675b2018-05-22 08:21:27466
charujain9893e252017-09-14 11:33:22467def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45468 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 13:21:44480 '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 09:13:45482 ]
483 return []
ehmaldonado4fb97462017-01-30 13:27:22484
Mirko Bonadeia51bbd82018-03-08 15:15:45485
Mirko Bonadeif0e0d752018-07-04 06:48:18486def _ReportFileAndLine(filename, line_num):
Mirko Bonadei8cc66952020-10-30 09:13:45487 """Default error formatter for _FindNewViolationsOfRule."""
488 return '%s (line %s)' % (filename, line_num)
Mirko Bonadeia51bbd82018-03-08 15:15:45489
490
Mirko Bonadei8cc66952020-10-30 09:13:45491def CheckNoWarningSuppressionFlagsAreAdded(gn_files,
492 input_api,
493 output_api,
Mirko Bonadeif0e0d752018-07-04 06:48:18494 error_formatter=_ReportFileAndLine):
Byoungchan Lee94f2ef22021-07-01 13:21:44495 """Ensure warning suppression flags are not added wihtout a reason."""
Mirko Bonadei8cc66952020-10-30 09:13:45496 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 06:48:18516
Mirko Bonadei9ce800d2019-02-05 15:48:13517
Mirko Bonadei8cc66952020-10-30 09:13:45518def CheckNoTestCaseUsageIsAdded(input_api,
519 output_api,
520 source_file_filter,
Mirko Bonadei9ce800d2019-02-05 15:48:13521 error_formatter=_ReportFileAndLine):
Mirko Bonadei8cc66952020-10-30 09:13:45522 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 15:48:13537
538
Mirko Bonadei8cc66952020-10-30 09:13:45539def CheckNoStreamUsageIsAdded(input_api,
540 output_api,
Artem Titov739351d2018-05-11 10:21:36541 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 06:48:18542 error_formatter=_ReportFileAndLine):
Mirko Bonadei8cc66952020-10-30 09:13:45543 """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 12:08:05571
Mirko Bonadei8cc66952020-10-30 09:13:45572 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 14:10:40578
Mirko Bonadei8cc66952020-10-30 09:13:45579 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 15:15:45590
Artem Titove92675b2018-05-22 08:21:27591
Mirko Bonadeia05d47e2018-05-09 09:03:38592def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45593 """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 10:52:27614
Artem Titove92675b2018-05-22 08:21:27615
Mirko Bonadei05691dd2019-10-22 14:34:24616def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45617 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 11:04:23636
Artem Titove92675b2018-05-22 08:21:27637
Mirko Bonadeif0e0d752018-07-04 06:48:18638def CheckGnChanges(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45639 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 12:51:08643
Mirko Bonadei8cc66952020-10-30 09:13:45644 gn_files = []
645 for f in input_api.AffectedSourceFiles(file_filter):
646 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 12:51:08647
Mirko Bonadei8cc66952020-10-30 09:13:45648 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 11:49:16661 output_api))
Mirko Bonadei8cc66952020-10-30 09:13:45662 return result
ehmaldonado5b1ba082016-09-02 12:51:08663
Artem Titove92675b2018-05-22 08:21:27664
Oleh Prypin920b6532017-10-05 09:28:51665def CheckGnGen(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45666 """Runs `gn gen --check` with default args to detect mismatches between
Oleh Prypin920b6532017-10-05 09:28:51667 #includes and dependencies in the BUILD.gn files, as well as general build
668 errors.
669 """
Mirko Bonadei8cc66952020-10-30 09:13:45670 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 13:21:44678 'Some #includes do not match the build dependency graph. '
679 'Please run:\n'
Mirko Bonadei8cc66952020-10-30 09:13:45680 ' gn gen --check <out_dir>',
681 long_text='\n\n'.join(errors))
682 ]
683 return []
Oleh Prypin920b6532017-10-05 09:28:51684
Artem Titove92675b2018-05-22 08:21:27685
Artem Titova04d1402018-05-11 09:23:00686def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 09:13:45687 """Runs checkdeps on #include statements added in this
kjellander@webrtc.org3bd41562014-09-01 11:06:37688 change. Breaking - rules is an error, breaking ! rules is a
689 warning.
690 """
Mirko Bonadei8cc66952020-10-30 09:13:45691 # Copied from Chromium's src/PRESUBMIT.py.
kjellander@webrtc.org3bd41562014-09-01 11:06:37692
Mirko Bonadei8cc66952020-10-30 09:13:45693 # 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:37709
Mirko Bonadei8cc66952020-10-30 09:13:45710 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:37714
Mirko Bonadei8cc66952020-10-30 09:13:45715 changed_lines = [line for _, line in f.ChangedContents()]
716 added_includes.append([f.LocalPath(), changed_lines])
kjellander@webrtc.org3bd41562014-09-01 11:06:37717
Mirko Bonadei8cc66952020-10-30 09:13:45718 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
kjellander@webrtc.org3bd41562014-09-01 11:06:37719
Mirko Bonadei8cc66952020-10-30 09:13:45720 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:37729
Mirko Bonadei8cc66952020-10-30 09:13:45730 results = []
731 if error_descriptions:
732 results.append(
733 output_api.PresubmitError(
Byoungchan Lee94f2ef22021-07-01 13:21:44734 '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 09:13:45739 if warning_descriptions:
740 results.append(
741 output_api.PresubmitPromptOrNotify(
Byoungchan Lee94f2ef22021-07-01 13:21:44742 '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 09:13:45748 return results
kjellander@webrtc.org3bd41562014-09-01 11:06:37749
Artem Titove92675b2018-05-22 08:21:27750
charujain9893e252017-09-14 11:33:22751def CheckCommitMessageBugEntry(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45752 """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 11:33:22777
Artem Titove92675b2018-05-22 08:21:27778
charujain9893e252017-09-14 11:33:22779def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45780 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 15:11:16781
782 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 13:12:35783 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 15:11:16784 a bug reference.
Mirko Bonadei61880182017-10-12 13:12:35785
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 15:11:16788 """
Mirko Bonadei8cc66952020-10-30 09:13:45789 if input_api.change.BugsFromDescription():
790 return []
791 else:
792 return [
793 output_api.PresubmitError(
Byoungchan Lee94f2ef22021-07-01 13:21:44794 '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 09:13:45798 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX'
799 )
800 ]
kjellander@webrtc.orge4158642014-08-06 09:11:18801
Artem Titove92675b2018-05-22 08:21:27802
Artem Titova04d1402018-05-11 09:23:00803def CheckJSONParseErrors(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 09:13:45804 """Check that JSON files do not contain syntax errors."""
kjellander569cf942016-02-11 13:02:59805
Mirko Bonadei8cc66952020-10-30 09:13:45806 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 13:02:59810
Mirko Bonadei8cc66952020-10-30 09:13:45811 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 13:02:59818
Mirko Bonadei8cc66952020-10-30 09:13:45819 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 13:02:59830
831
charujain9893e252017-09-14 11:33:22832def RunPythonTests(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45833 def Join(*args):
834 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
Henrik Kjellander8d3ad822015-05-26 17:52:05835
Mirko Bonadei8cc66952020-10-30 09:13:45836 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 17:52:05844
Mirko Bonadei8cc66952020-10-30 09:13:45845 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 17:52:05854
855
Artem Titova04d1402018-05-11 09:23:00856def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
857 source_file_filter):
Mirko Bonadei8cc66952020-10-30 09:13:45858 """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 12:38:01870
Mirko Bonadei8cc66952020-10-30 09:13:45871 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 12:38:01879
880
Mirko Bonadei92ea95e2017-09-15 04:47:31881def _LicenseHeader(input_api):
Mirko Bonadei8cc66952020-10-30 09:13:45882 """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 04:47:31889 r'All [Rr]ights [Rr]eserved\.\n'
Mirko Bonadei8cc66952020-10-30 09:13:45890 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 04:47:31894 r'found\n'
Mirko Bonadei8cc66952020-10-30 09:13:45895 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 04:47:31901
902
charujain9893e252017-09-14 11:33:22903def CommonChecks(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:45904 """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 13:21:44919
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(
Byoungchan Leeb58f7eb2021-07-15 18:04:44925 include_deletes=False, file_filter=python_file_filter)]
Byoungchan Lee94f2ef22021-07-01 13:21:44926
Mirko Bonadei8cc66952020-10-30 09:13:45927 results.extend(
928 input_api.canned_checks.RunPylint(
929 input_api,
930 output_api,
Byoungchan Lee94f2ef22021-07-01 13:21:44931 files_to_check=python_changed_files,
Mirko Bonadei8cc66952020-10-30 09:13:45932 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 13:02:59947
Mirko Bonadei8cc66952020-10-30 09:13:45948 # TODO(nisse): talk/ is no more, so make below checks simpler?
Byoungchan Lee94f2ef22021-07-01 13:21:44949 # 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 09:13:45952 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 06:03:56953
Byoungchan Lee94f2ef22021-07-01 13:21:44954 # .m and .mm files are ObjC files. For simplicity we will consider
955 # .h files in ObjC subdirectories ObjC headers.
Mirko Bonadei8cc66952020-10-30 09:13:45956 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 08:21:27969
Mirko Bonadei8cc66952020-10-30 09:13:45970 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 11:02:26993 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com',
994 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com',
Mirko Bonadei8cc66952020-10-30 09:13:45995 ]))
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(
Mirko Bonadeia6395132021-07-22 15:35:591045 CheckAssertUsage(input_api, output_api, non_third_party_sources))
1046 results.extend(
Mirko Bonadei8cc66952020-10-30 09:13:451047 CheckBannedAbslMakeUnique(input_api, output_api,
1048 non_third_party_sources))
1049 results.extend(
1050 CheckObjcApiSymbols(input_api, output_api, non_third_party_sources))
1051 return results
Mirko Bonadeia418e672018-10-24 11:57:251052
1053
1054def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:451055 """Check that 'include_rules' in api/DEPS is up to date.
Mirko Bonadei90490372018-10-26 11:17:471056
1057 The file api/DEPS must be kept up to date in order to avoid to avoid to
1058 include internal header from WebRTC's api/ headers.
1059
1060 This check is focused on ensuring that 'include_rules' contains a deny
1061 rule for each root level directory. More focused allow rules can be
1062 added to 'specific_include_rules'.
1063 """
Mirko Bonadei8cc66952020-10-30 09:13:451064 results = []
1065 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
1066 with open(api_deps) as f:
1067 deps_content = _ParseDeps(f.read())
Mirko Bonadeia418e672018-10-24 11:57:251068
Mirko Bonadei8cc66952020-10-30 09:13:451069 include_rules = deps_content.get('include_rules', [])
1070 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 11:57:251071
Mirko Bonadei8cc66952020-10-30 09:13:451072 # Only check top level directories affected by the current CL.
1073 dirs_to_check = set()
1074 for f in input_api.AffectedFiles():
1075 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
1076 if len(path_tokens) > 1:
1077 if (path_tokens[0] not in dirs_to_skip and os.path.isdir(
1078 os.path.join(input_api.PresubmitLocalPath(),
1079 path_tokens[0]))):
1080 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 11:57:251081
Mirko Bonadei8cc66952020-10-30 09:13:451082 missing_include_rules = set()
1083 for p in dirs_to_check:
1084 rule = '-%s' % p
1085 if rule not in include_rules:
1086 missing_include_rules.add(rule)
Mirko Bonadei90490372018-10-26 11:17:471087
Mirko Bonadei8cc66952020-10-30 09:13:451088 if missing_include_rules:
1089 error_msg = [
1090 'include_rules = [\n',
1091 ' ...\n',
1092 ]
Mirko Bonadeia418e672018-10-24 11:57:251093
Mirko Bonadei8cc66952020-10-30 09:13:451094 for r in sorted(missing_include_rules):
1095 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 11:57:251096
Mirko Bonadei8cc66952020-10-30 09:13:451097 error_msg.append(' ...\n')
1098 error_msg.append(']\n')
Mirko Bonadei90490372018-10-26 11:17:471099
Mirko Bonadei8cc66952020-10-30 09:13:451100 results.append(
1101 output_api.PresubmitError(
1102 'New root level directory detected! WebRTC api/ headers should '
1103 'not #include headers from \n'
1104 'the new directory, so please update "include_rules" in file\n'
1105 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
Mirko Bonadei90490372018-10-26 11:17:471106
Mirko Bonadei8cc66952020-10-30 09:13:451107 return results
1108
andrew@webrtc.org2442de12012-01-23 17:45:411109
Mirko Bonadei9fa8ef12019-09-17 17:14:131110def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 09:13:451111 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
1112 source_file_filter(f))
Mirko Bonadei9fa8ef12019-09-17 17:14:131113
Mirko Bonadei8cc66952020-10-30 09:13:451114 files = []
1115 for f in input_api.AffectedFiles(include_deletes=False,
1116 file_filter=file_filter):
1117 for _, line in f.ChangedContents():
1118 if 'absl::make_unique' in line:
1119 files.append(f)
1120 break
Mirko Bonadei9fa8ef12019-09-17 17:14:131121
Mirko Bonadei8cc66952020-10-30 09:13:451122 if len(files):
1123 return [
1124 output_api.PresubmitError(
1125 'Please use std::make_unique instead of absl::make_unique.\n'
1126 'Affected files:', files)
1127 ]
1128 return []
1129
Mirko Bonadei9fa8ef12019-09-17 17:14:131130
Mirko Bonadeid74c0e62020-07-16 19:57:011131def CheckObjcApiSymbols(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 09:13:451132 rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}',
1133 re.MULTILINE | re.DOTALL)
1134 file_filter = lambda f: (f.LocalPath().endswith(('.h')) and
1135 source_file_filter(f))
Mirko Bonadeid74c0e62020-07-16 19:57:011136
Mirko Bonadei8cc66952020-10-30 09:13:451137 files = []
1138 file_filter = lambda x: (input_api.FilterSourceFile(x) and
1139 source_file_filter(x))
1140 for f in input_api.AffectedSourceFiles(file_filter):
1141 if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath():
1142 continue
Niels Möller11ab77d2020-11-16 14:38:231143 if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'):
1144 continue
Mirko Bonadei8cc66952020-10-30 09:13:451145 contents = input_api.ReadFile(f)
1146 for match in rtc_objc_export.finditer(contents):
1147 export_block = match.group(0)
1148 if 'RTC_OBJC_TYPE' not in export_block:
1149 files.append(f.LocalPath())
Mirko Bonadeid74c0e62020-07-16 19:57:011150
Mirko Bonadei8cc66952020-10-30 09:13:451151 if len(files):
1152 return [
1153 output_api.PresubmitError(
1154 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() '
1155 + 'macro.\n\n' + 'For example:\n' +
1156 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' +
1157 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' +
1158 'Please fix the following files:', files)
1159 ]
1160 return []
1161
Mirko Bonadeid74c0e62020-07-16 19:57:011162
Mirko Bonadeia6395132021-07-22 15:35:591163def CheckAssertUsage(input_api, output_api, source_file_filter):
1164 pattern = input_api.re.compile(r'\bassert\(')
1165 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h', '.m', '.mm'))
1166 and source_file_filter(f))
1167
1168 files = []
1169 for f in input_api.AffectedFiles(include_deletes=False,
1170 file_filter=file_filter):
1171 for _, line in f.ChangedContents():
1172 if pattern.search(line):
1173 files.append(f.LocalPath())
1174 break
1175
1176 if len(files):
1177 return [
1178 output_api.PresubmitError(
1179 'Usage of assert() has been detected in the following files, '
1180 'please use RTC_DCHECK() instead.\n Files:', files)
1181 ]
1182 return []
1183
1184
tzika06bf852018-11-15 11:37:351185def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 09:13:451186 pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"',
1187 input_api.re.MULTILINE)
1188 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
1189 source_file_filter(f))
tzika06bf852018-11-15 11:37:351190
Mirko Bonadei8cc66952020-10-30 09:13:451191 files = []
1192 for f in input_api.AffectedFiles(include_deletes=False,
1193 file_filter=file_filter):
1194 contents = input_api.ReadFile(f)
1195 if pattern.search(contents):
1196 continue
1197 for _, line in f.ChangedContents():
1198 if 'absl::WrapUnique' in line:
1199 files.append(f)
1200 break
tzika06bf852018-11-15 11:37:351201
Mirko Bonadei8cc66952020-10-30 09:13:451202 if len(files):
1203 return [
1204 output_api.PresubmitError(
Byoungchan Lee94f2ef22021-07-01 13:21:441205 'Please include "absl/memory/memory.h" header for '
1206 'absl::WrapUnique.\nThis header may or may not be included '
1207 'transitively depending on the C++ standard version.', files)
Mirko Bonadei8cc66952020-10-30 09:13:451208 ]
1209 return []
1210
kjellander@webrtc.orge4158642014-08-06 09:11:181211
andrew@webrtc.org53df1362012-01-26 21:24:231212def CheckChangeOnUpload(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:451213 results = []
1214 results.extend(CommonChecks(input_api, output_api))
1215 results.extend(CheckGnGen(input_api, output_api))
1216 results.extend(
1217 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
1218 return results
niklase@google.comda159d62011-05-30 11:51:341219
kjellander@webrtc.orge4158642014-08-06 09:11:181220
andrew@webrtc.org2442de12012-01-23 17:45:411221def CheckChangeOnCommit(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:451222 results = []
1223 results.extend(CommonChecks(input_api, output_api))
1224 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
1225 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
1226 results.extend(
1227 input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api))
1228 results.extend(
1229 input_api.canned_checks.CheckChangeHasDescription(
1230 input_api, output_api))
1231 results.extend(CheckChangeHasBugField(input_api, output_api))
1232 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
1233 results.extend(
1234 input_api.canned_checks.CheckTreeIsOpen(
1235 input_api,
1236 output_api,
1237 json_url='http://webrtc-status.appspot.com/current?format=json'))
1238 return results
mbonadei74973ed2017-05-09 14:58:051239
1240
Artem Titova04d1402018-05-11 09:23:001241def CheckOrphanHeaders(input_api, output_api, source_file_filter):
Mirko Bonadei8cc66952020-10-30 09:13:451242 # We need to wait until we have an input_api object and use this
1243 # roundabout construct to import prebubmit_checks_lib because this file is
1244 # eval-ed and thus doesn't have __file__.
1245 error_msg = """{} should be listed in {}."""
1246 results = []
1247 exempt_paths = [
1248 os.path.join('tools_webrtc', 'ios', 'SDK'),
1249 ]
1250 with _AddToPath(
1251 input_api.os_path.join(input_api.PresubmitLocalPath(),
1252 'tools_webrtc', 'presubmit_checks_lib')):
1253 from check_orphan_headers import GetBuildGnPathFromFilePath
1254 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 14:58:051255
Mirko Bonadei8cc66952020-10-30 09:13:451256 file_filter = lambda x: input_api.FilterSourceFile(
1257 x, files_to_skip=exempt_paths) and source_file_filter(x)
1258 for f in input_api.AffectedSourceFiles(file_filter):
1259 if f.LocalPath().endswith('.h'):
1260 file_path = os.path.abspath(f.LocalPath())
1261 root_dir = os.getcwd()
1262 gn_file_path = GetBuildGnPathFromFilePath(file_path,
1263 os.path.exists, root_dir)
1264 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1265 if not in_build_gn:
1266 results.append(
1267 output_api.PresubmitError(
1268 error_msg.format(f.LocalPath(),
1269 os.path.relpath(gn_file_path))))
1270 return results
Mirko Bonadei960fd5b2017-06-29 12:59:361271
1272
Mirko Bonadei8cc66952020-10-30 09:13:451273def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api,
1274 source_file_filter):
1275 """Checks that all .proto files are terminated with a newline."""
1276 error_msg = 'File {} must end with exactly one newline.'
1277 results = []
1278 file_filter = lambda x: input_api.FilterSourceFile(
1279 x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x)
1280 for f in input_api.AffectedSourceFiles(file_filter):
1281 file_path = f.LocalPath()
1282 with open(file_path) as f:
1283 lines = f.readlines()
1284 if len(lines) > 0 and not lines[-1].endswith('\n'):
1285 results.append(
1286 output_api.PresubmitError(error_msg.format(file_path)))
1287 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231288
1289
1290def _ExtractAddRulesFromParsedDeps(parsed_deps):
Mirko Bonadei8cc66952020-10-30 09:13:451291 """Extract the rules that add dependencies from a parsed DEPS file.
Mirko Bonadei7e4ee6e2018-09-28 09:45:231292
1293 Args:
1294 parsed_deps: the locals dictionary from evaluating the DEPS file."""
Mirko Bonadei8cc66952020-10-30 09:13:451295 add_rules = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231296 add_rules.update([
Mirko Bonadei8cc66952020-10-30 09:13:451297 rule[1:] for rule in parsed_deps.get('include_rules', [])
Mirko Bonadei7e4ee6e2018-09-28 09:45:231298 if rule.startswith('+') or rule.startswith('!')
1299 ])
Mirko Bonadei8cc66952020-10-30 09:13:451300 for _, rules in parsed_deps.get('specific_include_rules', {}).iteritems():
1301 add_rules.update([
1302 rule[1:] for rule in rules
1303 if rule.startswith('+') or rule.startswith('!')
1304 ])
1305 return add_rules
Mirko Bonadei7e4ee6e2018-09-28 09:45:231306
1307
1308def _ParseDeps(contents):
Mirko Bonadei8cc66952020-10-30 09:13:451309 """Simple helper for parsing DEPS files."""
Mirko Bonadei7e4ee6e2018-09-28 09:45:231310
Mirko Bonadei8cc66952020-10-30 09:13:451311 # Stubs for handling special syntax in the root DEPS file.
1312 class VarImpl(object):
1313 def __init__(self, local_scope):
1314 self._local_scope = local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231315
Mirko Bonadei8cc66952020-10-30 09:13:451316 def Lookup(self, var_name):
1317 """Implements the Var syntax."""
1318 try:
1319 return self._local_scope['vars'][var_name]
1320 except KeyError:
1321 raise Exception('Var is not defined: %s' % var_name)
Mirko Bonadei7e4ee6e2018-09-28 09:45:231322
Mirko Bonadei8cc66952020-10-30 09:13:451323 local_scope = {}
1324 global_scope = {
1325 'Var': VarImpl(local_scope).Lookup,
1326 }
1327 exec contents in global_scope, local_scope
1328 return local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231329
1330
1331def _CalculateAddedDeps(os_path, old_contents, new_contents):
Mirko Bonadei8cc66952020-10-30 09:13:451332 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
Mirko Bonadei7e4ee6e2018-09-28 09:45:231333 a set of DEPS entries that we should look up.
1334
1335 For a directory (rather than a specific filename) we fake a path to
1336 a specific filename by adding /DEPS. This is chosen as a file that
1337 will seldom or never be subject to per-file include_rules.
1338 """
Mirko Bonadei8cc66952020-10-30 09:13:451339 # We ignore deps entries on auto-generated directories.
1340 auto_generated_dirs = ['grit', 'jni']
Mirko Bonadei7e4ee6e2018-09-28 09:45:231341
Mirko Bonadei8cc66952020-10-30 09:13:451342 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1343 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231344
Mirko Bonadei8cc66952020-10-30 09:13:451345 added_deps = new_deps.difference(old_deps)
Mirko Bonadei7e4ee6e2018-09-28 09:45:231346
Mirko Bonadei8cc66952020-10-30 09:13:451347 results = set()
1348 for added_dep in added_deps:
1349 if added_dep.split('/')[0] in auto_generated_dirs:
1350 continue
1351 # Assume that a rule that ends in .h is a rule for a specific file.
1352 if added_dep.endswith('.h'):
1353 results.add(added_dep)
1354 else:
1355 results.add(os_path.join(added_dep, 'DEPS'))
1356 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231357
1358
1359def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Mirko Bonadei8cc66952020-10-30 09:13:451360 """When a dependency prefixed with + is added to a DEPS file, we
Edward Lesmesbef08502021-02-05 22:08:321361 want to make sure that the change is reviewed by an OWNER of the
1362 target file or directory, to avoid layering violations from being
1363 introduced. This check verifies that this happens.
1364 """
Mirko Bonadei8cc66952020-10-30 09:13:451365 virtual_depended_on_files = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231366
Mirko Bonadei8cc66952020-10-30 09:13:451367 file_filter = lambda f: not input_api.re.match(
1368 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1369 for f in input_api.AffectedFiles(include_deletes=False,
1370 file_filter=file_filter):
1371 filename = input_api.os_path.basename(f.LocalPath())
1372 if filename == 'DEPS':
1373 virtual_depended_on_files.update(
1374 _CalculateAddedDeps(input_api.os_path,
1375 '\n'.join(f.OldContents()),
1376 '\n'.join(f.NewContents())))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231377
Mirko Bonadei8cc66952020-10-30 09:13:451378 if not virtual_depended_on_files:
1379 return []
Mirko Bonadei7e4ee6e2018-09-28 09:45:231380
Mirko Bonadei8cc66952020-10-30 09:13:451381 if input_api.is_committing:
1382 if input_api.tbr:
1383 return [
1384 output_api.PresubmitNotifyResult(
Byoungchan Lee94f2ef22021-07-01 13:21:441385 '--tbr was specified, skipping OWNERS check for DEPS '
1386 'additions'
Mirko Bonadei8cc66952020-10-30 09:13:451387 )
1388 ]
1389 if input_api.dry_run:
1390 return [
1391 output_api.PresubmitNotifyResult(
Byoungchan Lee94f2ef22021-07-01 13:21:441392 'This is a dry run, skipping OWNERS check for DEPS '
1393 'additions'
Mirko Bonadei8cc66952020-10-30 09:13:451394 )
1395 ]
1396 if not input_api.change.issue:
1397 return [
1398 output_api.PresubmitError(
1399 "DEPS approval by OWNERS check failed: this change has "
1400 "no change number, so we can't check it for approvals.")
1401 ]
1402 output = output_api.PresubmitError
Mirko Bonadei7e4ee6e2018-09-28 09:45:231403 else:
Mirko Bonadei8cc66952020-10-30 09:13:451404 output = output_api.PresubmitNotifyResult
Mirko Bonadei7e4ee6e2018-09-28 09:45:231405
Mirko Bonadei8cc66952020-10-30 09:13:451406 owner_email, reviewers = (
1407 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1408 input_api,
Edward Lesmesbef08502021-02-05 22:08:321409 None,
Mirko Bonadei8cc66952020-10-30 09:13:451410 approval_needed=input_api.is_committing))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231411
Mirko Bonadei8cc66952020-10-30 09:13:451412 owner_email = owner_email or input_api.change.author_email
1413
Edward Lesmesbef08502021-02-05 22:08:321414 approval_status = input_api.owners_client.GetFilesApprovalStatus(
1415 virtual_depended_on_files, reviewers.union([owner_email]), [])
1416 missing_files = [
1417 f for f in virtual_depended_on_files
1418 if approval_status[f] != input_api.owners_client.APPROVED]
Mirko Bonadei8cc66952020-10-30 09:13:451419
1420 # We strip the /DEPS part that was added by
1421 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1422 # directory.
1423 def StripDeps(path):
1424 start_deps = path.rfind('/DEPS')
1425 if start_deps != -1:
1426 return path[:start_deps]
1427 else:
1428 return path
1429
1430 unapproved_dependencies = [
1431 "'+%s'," % StripDeps(path) for path in missing_files
1432 ]
1433
1434 if unapproved_dependencies:
1435 output_list = [
1436 output(
Byoungchan Lee94f2ef22021-07-01 13:21:441437 'You need LGTM from owners of depends-on paths in DEPS that '
1438 ' were modified in this CL:\n %s' %
Mirko Bonadei8cc66952020-10-30 09:13:451439 '\n '.join(sorted(unapproved_dependencies)))
1440 ]
Edward Lesmesbef08502021-02-05 22:08:321441 suggested_owners = input_api.owners_client.SuggestOwners(
1442 missing_files, exclude=[owner_email])
Mirko Bonadei8cc66952020-10-30 09:13:451443 output_list.append(
1444 output('Suggested missing target path OWNERS:\n %s' %
1445 '\n '.join(suggested_owners or [])))
1446 return output_list
1447
1448 return []