blob: 51d87066b297ff792d46c890e91b931a3d5d1605 [file] [log] [blame]
Christoffer Jansson4e8a7732022-02-08 08:01:121#!/usr/bin/env vpython3
2
andrew@webrtc.org2442de12012-01-23 17:45:413# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
4#
5# Use of this source code is governed by a BSD-style license
6# that can be found in the LICENSE file in the root of the source
7# tree. An additional intellectual property rights grant can be found
8# in the file PATENTS. All contributing project authors may
9# be found in the AUTHORS file in the root of the source tree.
niklase@google.comda159d62011-05-30 11:51:3410
kjellander7439f972016-12-06 06:47:4611import json
kjellander@webrtc.orgaefe61a2014-12-08 13:00:3012import os
kjellander@webrtc.org85759802013-10-22 16:47:4013import re
kjellander@webrtc.org3bd41562014-09-01 11:06:3714import sys
Mirko Bonadei4dc4e252017-09-19 11:49:1615from collections import defaultdict
Oleh Prypin2f33a562017-10-04 18:17:5416from contextlib import contextmanager
kjellander@webrtc.org85759802013-10-22 16:47:4017
Christoffer Jansson4e8a7732022-02-08 08:01:1218# Runs PRESUBMIT.py in py3 mode by git cl presubmit.
19USE_PYTHON3 = True
20
oprypin2aa463f2017-03-23 10:17:0221# Files and directories that are *skipped* by cpplint in the presubmit script.
Mirko Bonadeifc17a782020-06-30 12:31:3722CPPLINT_EXCEPTIONS = [
Mirko Bonadei8cc66952020-10-30 09:13:4523 'api/video_codecs/video_decoder.h',
24 'common_types.cc',
25 'common_types.h',
26 'examples/objc',
27 'media/base/stream_params.h',
28 'media/base/video_common.h',
Mirko Bonadei8cc66952020-10-30 09:13:4529 'modules/audio_coding',
30 'modules/audio_device',
31 'modules/audio_processing',
32 'modules/desktop_capture',
33 'modules/include/module_common_types.h',
34 'modules/utility',
35 'modules/video_capture',
36 'p2p/base/pseudo_tcp.cc',
37 'p2p/base/pseudo_tcp.h',
Christoffer Jansson4e8a7732022-02-08 08:01:1238 'PRESUBMIT.py',
39 'presubmit_test_mocks.py',
40 'presubmit_test.py',
Mirko Bonadei8cc66952020-10-30 09:13:4541 'rtc_base',
42 'sdk/android/src/jni',
43 'sdk/objc',
44 'system_wrappers',
45 'test',
46 'tools_webrtc',
47 'voice_engine',
kjellander@webrtc.org0fcaf992015-11-26 14:24:5248]
49
Jeremy Leconte3d476f22023-10-17 13:04:2450PYLINT_OLD_STYLE = [
51 "PRESUBMIT.py",
Jeremy Leconte908c21c2023-10-17 14:02:5452 "tools_webrtc/autoroller/roll_deps.py",
Christoffer Dewerin81a91172024-04-08 07:26:4153 "tools_webrtc/android/build_aar.py",
54 "tools_webrtc/ios/build_ios_libs.py",
Jeremy Leconteef4d62e2024-05-16 11:27:0355 "tools_webrtc/mb/mb.py",
56 "tools_webrtc/mb/mb_unittest.py",
Jeremy Leconte3d476f22023-10-17 13:04:2457]
58
jbauchc4e3ead2016-02-19 08:25:5559# These filters will always be removed, even if the caller specifies a filter
60# set, as they are problematic or broken in some way.
61#
62# Justifications for each filter:
63# - build/c++11 : Rvalue ref checks are unreliable (false positives),
Mirko Bonadeifc17a782020-06-30 12:31:3764# include file and feature blocklists are
jbauchc4e3ead2016-02-19 08:25:5565# google3-specific.
Mirko Bonadei7b4b29a2023-11-29 16:01:3866# - readability/todo : WebRTC puts bug links, not usernames, in TODOs.
67# The new TODO style also doesn't match with this check.
Mirko Bonadeie92e2862020-05-29 13:23:0968# - runtime/references : Mutable references are not banned by the Google
69# C++ style guide anymore (starting from May 2020).
kjellandere5a87a52016-04-27 09:32:1270# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
71# all move-related errors).
Mirko Bonadeifc17a782020-06-30 12:31:3772DISABLED_LINT_FILTERS = [
Mirko Bonadei8cc66952020-10-30 09:13:4573 '-build/c++11',
Mirko Bonadei7b4b29a2023-11-29 16:01:3874 '-readability/todo',
Mirko Bonadei8cc66952020-10-30 09:13:4575 '-runtime/references',
76 '-whitespace/operators',
jbauchc4e3ead2016-02-19 08:25:5577]
78
kjellanderfd595232015-12-04 10:44:0979# List of directories of "supported" native APIs. That means changes to headers
80# will be done in a compatible way following this scheme:
81# 1. Non-breaking changes are made.
82# 2. The old APIs as marked as deprecated (with comments).
83# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
84# webrtc-users@google.com (internal list).
85# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-03 07:56:1486NATIVE_API_DIRS = (
Mirko Bonadei8cc66952020-10-30 09:13:4587 'api', # All subdirectories of api/ are included as well.
88 'media/base',
89 'media/engine',
90 'modules/audio_device/include',
91 'pc',
kjellanderdd705472016-06-09 18:17:2792)
Mirko Bonadei4dc4e252017-09-19 11:49:1693
kjellanderdd705472016-06-09 18:17:2794# These directories should not be used but are maintained only to avoid breaking
95# some legacy downstream code.
96LEGACY_API_DIRS = (
Mirko Bonadei8cc66952020-10-30 09:13:4597 'common_audio/include',
98 'modules/audio_coding/include',
99 'modules/audio_processing/include',
100 'modules/congestion_controller/include',
101 'modules/include',
102 'modules/remote_bitrate_estimator/include',
103 'modules/rtp_rtcp/include',
104 'modules/rtp_rtcp/source',
105 'modules/utility/include',
106 'modules/video_coding/codecs/h264/include',
107 'modules/video_coding/codecs/vp8/include',
108 'modules/video_coding/codecs/vp9/include',
109 'modules/video_coding/include',
110 'rtc_base',
111 'system_wrappers/include',
kjellander53047c92015-12-03 07:56:14112)
Mirko Bonadei4dc4e252017-09-19 11:49:16113
Karl Wibergd4f01c12017-11-10 09:55:45114# NOTE: The set of directories in API_DIRS should be the same as those
115# listed in the table in native-api.md.
kjellanderdd705472016-06-09 18:17:27116API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-03 07:56:14117
Mirko Bonadei4dc4e252017-09-19 11:49:16118# TARGET_RE matches a GN target, and extracts the target name and the contents.
Mirko Bonadei2dcf3482020-06-05 12:30:41119TARGET_RE = re.compile(
Mirko Bonadei8cc66952020-10-30 09:13:45120 r'(?P<indent>\s*)(?P<target_type>\w+)\("(?P<target_name>\w+)"\) {'
121 r'(?P<target_contents>.*?)'
122 r'(?P=indent)}', re.MULTILINE | re.DOTALL)
Mirko Bonadei4dc4e252017-09-19 11:49:16123
124# SOURCES_RE matches a block of sources inside a GN target.
125SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
126 re.MULTILINE | re.DOTALL)
127
Mirko Bonadei2dcf3482020-06-05 12:30:41128# DEPS_RE matches a block of sources inside a GN target.
129DEPS_RE = re.compile(r'\bdeps \+?= \[(?P<deps>.*?)\]',
130 re.MULTILINE | re.DOTALL)
131
Philipp Hancke0c2a9ca2021-08-11 10:00:27132# FILE_PATH_RE matches a file path.
Mirko Bonadei4dc4e252017-09-19 11:49:16133FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
134
kjellander53047c92015-12-03 07:56:14135
Oleh Prypin2f33a562017-10-04 18:17:54136@contextmanager
137def _AddToPath(*paths):
Jeremy Leconte3d476f22023-10-17 13:04:24138 original_sys_path = sys.path
139 sys.path.extend(paths)
140 try:
141 yield
142 finally:
143 # Restore sys.path to what it was before.
144 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 13:27:22145
146
charujain9893e252017-09-14 11:33:22147def VerifyNativeApiHeadersListIsValid(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24148 """Ensures the list of native API header directories is up to date."""
149 non_existing_paths = []
150 native_api_full_paths = [
151 input_api.os_path.join(input_api.PresubmitLocalPath(),
152 *path.split('/')) for path in API_DIRS
Mirko Bonadei8cc66952020-10-30 09:13:45153 ]
Jeremy Leconte3d476f22023-10-17 13:04:24154 for path in native_api_full_paths:
155 if not os.path.isdir(path):
156 non_existing_paths.append(path)
157 if non_existing_paths:
158 return [
159 output_api.PresubmitError(
160 'Directories to native API headers have changed which has made '
161 'the list in PRESUBMIT.py outdated.\nPlease update it to the '
162 'current location of our native APIs.', non_existing_paths)
163 ]
164 return []
kjellander53047c92015-12-03 07:56:14165
Artem Titove92675b2018-05-22 08:21:27166
kjellanderc88b5d52017-04-05 13:42:43167API_CHANGE_MSG = """
kwibergeb133022016-04-07 14:41:48168You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 12:13:23169 1. Make compatible changes that don't break existing clients. Usually
170 this is done by keeping the existing method signatures unchanged.
Harald Alvestrandf055f9b2024-10-08 20:00:00171 2. Mark the old stuff as deprecated (use the [[deprecated]] attribute or
172 the ABSL_DEPRECATE_AND_INLINE macro).
kwibergeb133022016-04-07 14:41:48173 3. Create a timeline and plan for when the deprecated stuff will be
174 removed. (The amount of time we give users to change their code
175 should be informed by how much work it is for them. If they just
176 need to replace one name with another or something equally
177 simple, 1-2 weeks might be good; if they need to do serious work,
178 up to 3 months may be called for.)
179 4. Update/inform existing downstream code owners to stop using the
180 deprecated stuff. (Send announcements to
181 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
182 5. Remove the deprecated stuff, once the agreed-upon amount of time
183 has passed.
184Related files:
185"""
kjellander53047c92015-12-03 07:56:14186
Artem Titove92675b2018-05-22 08:21:27187
charujain9893e252017-09-14 11:33:22188def CheckNativeApiHeaderChanges(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24189 """Checks to remind proper changing of native APIs."""
Dor Hen59d592e2024-09-16 12:48:45190 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24191 source_file_filter = lambda x: input_api.FilterSourceFile(
192 x, files_to_check=[r'.+\.(gn|gni|h)$'])
193 for f in input_api.AffectedSourceFiles(source_file_filter):
194 for path in API_DIRS:
195 dn = os.path.dirname(f.LocalPath())
196 if path == 'api':
197 # Special case: Subdirectories included.
198 if dn == 'api' or dn.startswith('api/'):
Dor Hen59d592e2024-09-16 12:48:45199 files.add(f.LocalPath())
Jeremy Leconte3d476f22023-10-17 13:04:24200 else:
201 # Normal case: Subdirectories not included.
202 if dn == path:
Dor Hen59d592e2024-09-16 12:48:45203 files.add(f.LocalPath())
kjellander53047c92015-12-03 07:56:14204
Jeremy Leconte3d476f22023-10-17 13:04:24205 if files:
Dor Hen59d592e2024-09-16 12:48:45206 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, list(files))]
Jeremy Leconte3d476f22023-10-17 13:04:24207 return []
kjellander53047c92015-12-03 07:56:14208
kjellander@webrtc.org0fcaf992015-11-26 14:24:52209
Mirko Bonadei8cc66952020-10-30 09:13:45210def CheckNoIOStreamInHeaders(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24211 """Checks to make sure no .h files include <iostream>."""
Dor Hen59d592e2024-09-16 12:48:45212 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24213 pattern = input_api.re.compile(r'^#include\s*<iostream>',
214 input_api.re.MULTILINE)
215 file_filter = lambda x: (input_api.FilterSourceFile(x) and
216 source_file_filter(x))
217 for f in input_api.AffectedSourceFiles(file_filter):
218 if not f.LocalPath().endswith('.h'):
219 continue
220 contents = input_api.ReadFile(f)
221 if pattern.search(contents):
Dor Hen59d592e2024-09-16 12:48:45222 files.add(f)
kjellander@webrtc.org51198f12012-02-21 17:53:46223
Jeremy Leconte3d476f22023-10-17 13:04:24224 if len(files) > 0:
225 return [
226 output_api.PresubmitError(
227 'Do not #include <iostream> in header files, since it inserts '
228 'static initialization into every file including the header. '
229 'Instead, #include <ostream>. See http://crbug.com/94794',
Dor Hen59d592e2024-09-16 12:48:45230 list(files))
Jeremy Leconte3d476f22023-10-17 13:04:24231 ]
232 return []
kjellander@webrtc.org51198f12012-02-21 17:53:46233
kjellander@webrtc.orge4158642014-08-06 09:11:18234
Mirko Bonadei8cc66952020-10-30 09:13:45235def CheckNoPragmaOnce(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24236 """Make sure that banned functions are not used."""
Dor Hen59d592e2024-09-16 12:48:45237 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24238 pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE)
239 file_filter = lambda x: (input_api.FilterSourceFile(x) and
240 source_file_filter(x))
241 for f in input_api.AffectedSourceFiles(file_filter):
242 if not f.LocalPath().endswith('.h'):
243 continue
244 contents = input_api.ReadFile(f)
245 if pattern.search(contents):
Dor Hen59d592e2024-09-16 12:48:45246 files.add(f)
kjellander6aeef742017-02-20 09:13:18247
Jeremy Leconte3d476f22023-10-17 13:04:24248 if files:
249 return [
250 output_api.PresubmitError(
251 'Do not use #pragma once in header files.\n'
252 'See http://www.chromium.org/developers/coding-style'
Dor Hen59d592e2024-09-16 12:48:45253 '#TOC-File-headers', list(files))
Jeremy Leconte3d476f22023-10-17 13:04:24254 ]
255 return []
Christoffer Jansson4e8a7732022-02-08 08:01:12256
kjellander6aeef742017-02-20 09:13:18257
Byoungchan Lee94f2ef22021-07-01 13:21:44258def CheckNoFRIEND_TEST(# pylint: disable=invalid-name
Mirko Bonadei8cc66952020-10-30 09:13:45259 input_api,
Byoungchan Lee94f2ef22021-07-01 13:21:44260 output_api,
Mirko Bonadei8cc66952020-10-30 09:13:45261 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24262 """Make sure that gtest's FRIEND_TEST() macro is not used, the
kjellander@webrtc.org51198f12012-02-21 17:53:46263 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
264 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
Jeremy Leconte3d476f22023-10-17 13:04:24265 problems = []
kjellander@webrtc.org51198f12012-02-21 17:53:46266
Jeremy Leconte3d476f22023-10-17 13:04:24267 file_filter = lambda f: (f.LocalPath().endswith(
268 ('.cc', '.h')) and source_file_filter(f))
269 for f in input_api.AffectedFiles(file_filter=file_filter):
270 for line_num, line in f.ChangedContents():
271 if 'FRIEND_TEST(' in line:
272 problems.append(' %s:%d' % (f.LocalPath(), line_num))
kjellander@webrtc.org51198f12012-02-21 17:53:46273
Jeremy Leconte3d476f22023-10-17 13:04:24274 if not problems:
275 return []
276 return [
277 output_api.PresubmitPromptWarning(
278 'WebRTC\'s code should not use gtest\'s FRIEND_TEST() macro. '
279 'Include testsupport/gtest_prod_util.h and use '
280 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))
281 ]
kjellander@webrtc.org51198f12012-02-21 17:53:46282
kjellander@webrtc.orge4158642014-08-06 09:11:18283
Mirko Bonadeifc17a782020-06-30 12:31:37284def IsLintDisabled(disabled_paths, file_path):
Jeremy Leconte3d476f22023-10-17 13:04:24285 """ Checks if a file is disabled for lint check."""
286 for path in disabled_paths:
287 if file_path == path or os.path.dirname(file_path).startswith(path):
288 return True
289 return False
kjellander@webrtc.org0fcaf992015-11-26 14:24:52290
291
charujain9893e252017-09-14 11:33:22292def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 09:23:00293 source_file_filter=None):
Jeremy Leconte3d476f22023-10-17 13:04:24294 """Checks that all new or non-exempt .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 11:33:22295 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46296 depot_tools/presubmit_canned_checks.py but has less filters and only checks
297 added files."""
Jeremy Leconte3d476f22023-10-17 13:04:24298 result = []
kjellander@webrtc.org51198f12012-02-21 17:53:46299
Jeremy Leconte3d476f22023-10-17 13:04:24300 # Initialize cpplint.
301 import cpplint
302 # Access to a protected member _XX of a client class
303 # pylint: disable=W0212
304 cpplint._cpplint_state.ResetErrorCounts()
kjellander@webrtc.org51198f12012-02-21 17:53:46305
Jeremy Leconte3d476f22023-10-17 13:04:24306 lint_filters = cpplint._Filters()
307 lint_filters.extend(DISABLED_LINT_FILTERS)
308 cpplint._SetFilters(','.join(lint_filters))
jbauchc4e3ead2016-02-19 08:25:55309
Jeremy Leconte3d476f22023-10-17 13:04:24310 # Create a platform independent exempt list for cpplint.
311 disabled_paths = [
312 input_api.os_path.join(*path.split('/')) for path in CPPLINT_EXCEPTIONS
313 ]
kjellander@webrtc.org0fcaf992015-11-26 14:24:52314
Jeremy Leconte3d476f22023-10-17 13:04:24315 # Use the strictest verbosity level for cpplint.py (level 1) which is the
316 # default when running cpplint.py from command line. To make it possible to
317 # work with not-yet-converted code, we're only applying it to new (or
318 # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS.
319 verbosity_level = 1
Dor Hen59d592e2024-09-16 12:48:45320 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24321 for f in input_api.AffectedSourceFiles(source_file_filter):
322 # Note that moved/renamed files also count as added.
323 if f.Action() == 'A' or not IsLintDisabled(disabled_paths,
324 f.LocalPath()):
Dor Hen59d592e2024-09-16 12:48:45325 files.add(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23326
Jeremy Leconte3d476f22023-10-17 13:04:24327 for file_name in files:
328 cpplint.ProcessFile(file_name, verbosity_level)
kjellander@webrtc.org51198f12012-02-21 17:53:46329
Jeremy Leconte3d476f22023-10-17 13:04:24330 if cpplint._cpplint_state.error_count > 0:
331 if input_api.is_committing:
332 res_type = output_api.PresubmitError
333 else:
334 res_type = output_api.PresubmitPromptWarning
335 result = [res_type('Changelist failed cpplint.py check.')]
kjellander@webrtc.org51198f12012-02-21 17:53:46336
Jeremy Leconte3d476f22023-10-17 13:04:24337 return result
kjellander@webrtc.org51198f12012-02-21 17:53:46338
Artem Titove92675b2018-05-22 08:21:27339
charujain9893e252017-09-14 11:33:22340def CheckNoSourcesAbove(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24341 # Disallow referencing source files with paths above the GN file location.
342 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
343 re.MULTILINE | re.DOTALL)
344 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
345 violating_gn_files = set()
Dor Hen59d592e2024-09-16 12:48:45346 violating_source_entries = set()
Jeremy Leconte3d476f22023-10-17 13:04:24347 for gn_file in gn_files:
348 contents = input_api.ReadFile(gn_file)
349 for source_block_match in source_pattern.finditer(contents):
350 # Find all source list entries starting with ../ in the source block
351 # (exclude overrides entries).
352 for file_list_match in file_pattern.finditer(
353 source_block_match.group(1)):
354 source_file = file_list_match.group(1)
355 if 'overrides/' not in source_file:
Dor Hen59d592e2024-09-16 12:48:45356 violating_source_entries.add(source_file)
Jeremy Leconte3d476f22023-10-17 13:04:24357 violating_gn_files.add(gn_file)
358 if violating_gn_files:
359 return [
360 output_api.PresubmitError(
361 'Referencing source files above the directory of the GN file '
362 'is not allowed. Please introduce new GN targets in the proper '
363 'location instead.\n'
364 'Invalid source entries:\n'
365 '%s\n'
366 'Violating GN files:' % '\n'.join(violating_source_entries),
Dor Hen59d592e2024-09-16 12:48:45367 items=list(violating_gn_files))
Jeremy Leconte3d476f22023-10-17 13:04:24368 ]
369 return []
ehmaldonado5b1ba082016-09-02 12:51:08370
Artem Titove92675b2018-05-22 08:21:27371
Mirko Bonadei4dc4e252017-09-19 11:49:16372def CheckNoMixingSources(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24373 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
Mirko Bonadei4dc4e252017-09-19 11:49:16374
375 See bugs.webrtc.org/7743 for more context.
376 """
Jeremy Leconte3d476f22023-10-17 13:04:24377 def _MoreThanOneSourceUsed(*sources_lists):
378 sources_used = 0
379 for source_list in sources_lists:
380 if len(source_list) > 0:
381 sources_used += 1
382 return sources_used > 1
Artem Titove92675b2018-05-22 08:21:27383
Jeremy Leconte3d476f22023-10-17 13:04:24384 errors = defaultdict(lambda: [])
385 for gn_file in gn_files:
386 gn_file_content = input_api.ReadFile(gn_file)
387 for target_match in TARGET_RE.finditer(gn_file_content):
388 # list_of_sources is a list of tuples of the form
389 # (c_files, cc_files, objc_files) that keeps track of all the
390 # sources defined in a target. A GN target can have more that
391 # on definition of sources (since it supports if/else statements).
392 # E.g.:
393 # rtc_static_library("foo") {
394 # if (is_win) {
395 # sources = [ "foo.cc" ]
396 # } else {
397 # sources = [ "foo.mm" ]
398 # }
399 # }
400 # This is allowed and the presubmit check should support this case.
401 list_of_sources = []
402 c_files = []
403 cc_files = []
404 objc_files = []
405 target_name = target_match.group('target_name')
406 target_contents = target_match.group('target_contents')
407 for sources_match in SOURCES_RE.finditer(target_contents):
408 if '+=' not in sources_match.group(0):
409 if c_files or cc_files or objc_files:
410 list_of_sources.append((c_files, cc_files, objc_files))
411 c_files = []
412 cc_files = []
413 objc_files = []
414 for file_match in FILE_PATH_RE.finditer(
415 sources_match.group(1)):
416 file_path = file_match.group('file_path')
417 extension = file_match.group('extension')
418 if extension == '.c':
419 c_files.append(file_path + extension)
420 if extension == '.cc':
421 cc_files.append(file_path + extension)
422 if extension in ['.m', '.mm']:
423 objc_files.append(file_path + extension)
Mirko Bonadei4dc4e252017-09-19 11:49:16424 list_of_sources.append((c_files, cc_files, objc_files))
Jeremy Leconte3d476f22023-10-17 13:04:24425 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
426 if _MoreThanOneSourceUsed(c_files_list, cc_files_list,
427 objc_files_list):
428 all_sources = sorted(c_files_list + cc_files_list +
429 objc_files_list)
430 errors[gn_file.LocalPath()].append(
431 (target_name, all_sources))
432 if errors:
433 return [
434 output_api.PresubmitError(
435 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
436 'Please create a separate target for each collection of '
437 'sources.\n'
438 'Mixed sources: \n'
439 '%s\n'
440 'Violating GN files:\n%s\n' %
441 (json.dumps(errors, indent=2), '\n'.join(list(errors.keys()))))
442 ]
443 return []
kjellander7439f972016-12-06 06:47:46444
Artem Titove92675b2018-05-22 08:21:27445
charujain9893e252017-09-14 11:33:22446def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24447 cwd = input_api.PresubmitLocalPath()
448 with _AddToPath(
449 input_api.os_path.join(cwd, 'tools_webrtc',
450 'presubmit_checks_lib')):
451 from check_package_boundaries import CheckPackageBoundaries
452 build_files = [
453 os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files
Mirko Bonadei8cc66952020-10-30 09:13:45454 ]
Jeremy Leconte3d476f22023-10-17 13:04:24455 errors = CheckPackageBoundaries(cwd, build_files)[:5]
456 if errors:
457 return [
458 output_api.PresubmitError(
459 'There are package boundary violations in the following GN '
460 'files:',
461 long_text='\n\n'.join(str(err) for err in errors))
462 ]
463 return []
ehmaldonado4fb97462017-01-30 13:27:22464
Mirko Bonadeia51bbd82018-03-08 15:15:45465
Mirko Bonadeif0e0d752018-07-04 06:48:18466def _ReportFileAndLine(filename, line_num):
Jeremy Leconte3d476f22023-10-17 13:04:24467 """Default error formatter for _FindNewViolationsOfRule."""
468 return '%s (line %s)' % (filename, line_num)
Mirko Bonadeia51bbd82018-03-08 15:15:45469
470
Mirko Bonadei8cc66952020-10-30 09:13:45471def CheckNoWarningSuppressionFlagsAreAdded(gn_files,
472 input_api,
473 output_api,
Mirko Bonadeif0e0d752018-07-04 06:48:18474 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24475 """Ensure warning suppression flags are not added without a reason."""
476 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
477 'in WebRTC.\n'
478 'If you are not adding this code (e.g. you are just moving '
479 'existing code) or you want to add an exception,\n'
480 'you can add a comment on the line that causes the problem:\n\n'
481 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
482 '\n'
483 'Affected files:\n')
484 errors = [] # 2-element tuples with (file, line number)
485 clang_warn_re = input_api.re.compile(
486 r'//build/config/clang:extra_warnings')
487 # pylint: disable-next=fixme
488 no_presubmit_re = input_api.re.compile(
489 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
490 for f in gn_files:
491 for line_num, line in f.ChangedContents():
492 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
493 errors.append(error_formatter(f.LocalPath(), line_num))
494 if errors:
495 return [output_api.PresubmitError(msg, errors)]
496 return []
Mirko Bonadeif0e0d752018-07-04 06:48:18497
Mirko Bonadei9ce800d2019-02-05 15:48:13498
Mirko Bonadei8cc66952020-10-30 09:13:45499def CheckNoTestCaseUsageIsAdded(input_api,
500 output_api,
501 source_file_filter,
Mirko Bonadei9ce800d2019-02-05 15:48:13502 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24503 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
504 'new API: https://github.com/google/googletest/blob/master/'
505 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
506 'Affected files:\n')
507 errors = [] # 2-element tuples with (file, line number)
508 test_case_re = input_api.re.compile(r'TEST_CASE')
509 file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith(
510 '.cc'))
511 for f in input_api.AffectedSourceFiles(file_filter):
512 for line_num, line in f.ChangedContents():
513 if test_case_re.search(line):
514 errors.append(error_formatter(f.LocalPath(), line_num))
515 if errors:
516 return [output_api.PresubmitError(error_msg, errors)]
517 return []
Mirko Bonadei9ce800d2019-02-05 15:48:13518
519
Mirko Bonadei8cc66952020-10-30 09:13:45520def CheckNoStreamUsageIsAdded(input_api,
521 output_api,
Artem Titov739351d2018-05-11 10:21:36522 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 06:48:18523 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24524 """Make sure that no more dependencies on stringstream are added."""
525 error_msg = (
526 'Usage of <sstream>, <istream> and <ostream> in WebRTC is '
527 'deprecated.\n'
528 'This includes the following types:\n'
529 'std::istringstream, std::ostringstream, std::wistringstream, '
530 'std::wostringstream,\n'
531 'std::wstringstream, std::ostream, std::wostream, std::istream,'
532 'std::wistream,\n'
533 'std::iostream, std::wiostream.\n'
534 'If you are not adding this code (e.g. you are just moving '
535 'existing code),\n'
536 'you can add a comment on the line that causes the problem:\n\n'
537 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
538 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
539 '\n'
540 'If you are adding new code, consider using '
541 'rtc::SimpleStringBuilder\n'
542 '(in rtc_base/strings/string_builder.h).\n'
543 'Affected files:\n')
544 errors = [] # 2-element tuples with (file, line number)
545 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
546 usage_re = input_api.re.compile(
547 r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
548 no_presubmit_re = input_api.re.compile(
549 r'// no-presubmit-check TODO\(webrtc:8982\)')
550 file_filter = lambda x: (input_api.FilterSourceFile(x) and
551 source_file_filter(x))
Mirko Bonadei571791a2019-05-07 12:08:05552
Jeremy Leconte3d476f22023-10-17 13:04:24553 def _IsException(file_path):
554 is_test = any(
555 file_path.endswith(x) for x in
556 ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc'])
557 return (file_path.startswith('examples')
558 or file_path.startswith('test') or is_test)
Patrik Höglund2ea27962020-01-13 14:10:40559
Jeremy Leconte3d476f22023-10-17 13:04:24560 for f in input_api.AffectedSourceFiles(file_filter):
561 # Usage of stringstream is allowed under examples/ and in tests.
562 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
563 continue
564 for line_num, line in f.ChangedContents():
565 if ((include_re.search(line) or usage_re.search(line))
566 and not no_presubmit_re.search(line)):
567 errors.append(error_formatter(f.LocalPath(), line_num))
568 if errors:
569 return [output_api.PresubmitError(error_msg, errors)]
570 return []
Mirko Bonadeia51bbd82018-03-08 15:15:45571
Artem Titove92675b2018-05-22 08:21:27572
Mirko Bonadeia05d47e2018-05-09 09:03:38573def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24574 """Checks that public_deps is not used without a good reason."""
575 result = []
576 no_presubmit_check_re = input_api.re.compile(
577 r'# no-presubmit-check TODO\(webrtc:\d+\)')
578 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
579 'because it doesn\'t map well to downstream build systems.\n'
580 'Used in: %s (line %d).\n'
581 'If you are not adding this code (e.g. you are just moving '
582 'existing code) or you have a good reason, you can add this '
583 'comment (verbatim) on the line that causes the problem:\n\n'
584 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
585 for affected_file in gn_files:
586 for (line_number, affected_line) in affected_file.ChangedContents():
587 if 'public_deps' in affected_line:
588 surpressed = no_presubmit_check_re.search(affected_line)
589 if not surpressed:
590 result.append(
591 output_api.PresubmitError(
592 error_msg %
593 (affected_file.LocalPath(), line_number)))
594 return result
Mirko Bonadei5c1ad592017-12-12 10:52:27595
Artem Titove92675b2018-05-22 08:21:27596
Mirko Bonadei05691dd2019-10-22 14:34:24597def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24598 result = []
599 error_msg = (
600 'check_includes overrides are not allowed since it can cause '
601 'incorrect dependencies to form. It effectively means that your '
602 'module can include any .h file without depending on its '
603 'corresponding target. There are some exceptional cases when '
604 'this is allowed: if so, get approval from a .gn owner in the '
605 'root OWNERS file.\n'
606 'Used in: %s (line %d).')
607 # pylint: disable-next=fixme
608 no_presubmit_re = input_api.re.compile(
609 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
610 for affected_file in gn_files:
611 for (line_number, affected_line) in affected_file.ChangedContents():
612 if ('check_includes' in affected_line
613 and not no_presubmit_re.search(affected_line)):
614 result.append(
615 output_api.PresubmitError(
616 error_msg % (affected_file.LocalPath(), line_number)))
617 return result
Patrik Höglund6f491062018-01-11 11:04:23618
Artem Titove92675b2018-05-22 08:21:27619
Mirko Bonadeif0e0d752018-07-04 06:48:18620def CheckGnChanges(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24621 file_filter = lambda x: (input_api.FilterSourceFile(
622 x,
623 files_to_check=(r'.+\.(gn|gni)$', ),
624 files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', )))
ehmaldonado5b1ba082016-09-02 12:51:08625
Dor Hen59d592e2024-09-16 12:48:45626 gn_files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24627 for f in input_api.AffectedSourceFiles(file_filter):
Dor Hen59d592e2024-09-16 12:48:45628 gn_files.add(f)
ehmaldonado5b1ba082016-09-02 12:51:08629
Jeremy Leconte3d476f22023-10-17 13:04:24630 result = []
631 if gn_files:
632 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
633 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
Jeremy Leconte3d476f22023-10-17 13:04:24634 result.extend(
635 CheckNoPackageBoundaryViolations(input_api, gn_files, output_api))
636 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api,
637 output_api))
638 result.extend(
639 CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
640 result.extend(
641 CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
642 output_api))
643 return result
ehmaldonado5b1ba082016-09-02 12:51:08644
Artem Titove92675b2018-05-22 08:21:27645
Oleh Prypin920b6532017-10-05 09:28:51646def CheckGnGen(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24647 """Runs `gn gen --check` with default args to detect mismatches between
Oleh Prypin920b6532017-10-05 09:28:51648 #includes and dependencies in the BUILD.gn files, as well as general build
649 errors.
650 """
Jeremy Leconte3d476f22023-10-17 13:04:24651 with _AddToPath(
652 input_api.os_path.join(input_api.PresubmitLocalPath(),
653 'tools_webrtc', 'presubmit_checks_lib')):
Jeremy Leconte53291d42024-08-01 13:45:35654 from build_helpers import run_gn_check
655 errors = run_gn_check(input_api.change.RepositoryRoot())[:5]
Jeremy Leconte3d476f22023-10-17 13:04:24656 if errors:
657 return [
658 output_api.PresubmitPromptWarning(
659 'Some #includes do not match the build dependency graph. '
660 'Please run:\n'
661 ' gn gen --check <out_dir>',
662 long_text='\n\n'.join(errors))
663 ]
664 return []
Oleh Prypin920b6532017-10-05 09:28:51665
Artem Titove92675b2018-05-22 08:21:27666
Artem Titova04d1402018-05-11 09:23:00667def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24668 """Runs checkdeps on #include statements added in this
kjellander@webrtc.org3bd41562014-09-01 11:06:37669 change. Breaking - rules is an error, breaking ! rules is a
670 warning.
671 """
Jeremy Leconte3d476f22023-10-17 13:04:24672 # Copied from Chromium's src/PRESUBMIT.py.
kjellander@webrtc.org3bd41562014-09-01 11:06:37673
Jeremy Leconte3d476f22023-10-17 13:04:24674 # We need to wait until we have an input_api object and use this
675 # roundabout construct to import checkdeps because this file is
676 # eval-ed and thus doesn't have __file__.
Gavin Makfa934322024-07-26 06:33:11677 repo_root = input_api.change.RepositoryRoot()
678 checkdeps_path = input_api.os_path.join(repo_root, 'buildtools',
Jeremy Leconte3d476f22023-10-17 13:04:24679 'checkdeps')
680 if not os.path.exists(checkdeps_path):
681 return [
682 output_api.PresubmitError(
683 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
684 'download all the DEPS entries?' % checkdeps_path)
685 ]
686 with _AddToPath(checkdeps_path):
687 import checkdeps
688 from cpp_checker import CppChecker
689 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37690
Jeremy Leconte3d476f22023-10-17 13:04:24691 added_includes = []
692 for f in input_api.AffectedFiles(file_filter=source_file_filter):
693 if not CppChecker.IsCppFile(f.LocalPath()):
694 continue
kjellander@webrtc.org3bd41562014-09-01 11:06:37695
Jeremy Leconte3d476f22023-10-17 13:04:24696 changed_lines = [line for _, line in f.ChangedContents()]
697 added_includes.append([f.LocalPath(), changed_lines])
kjellander@webrtc.org3bd41562014-09-01 11:06:37698
Jeremy Leconte3d476f22023-10-17 13:04:24699 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
kjellander@webrtc.org3bd41562014-09-01 11:06:37700
Jeremy Leconte3d476f22023-10-17 13:04:24701 error_descriptions = []
702 warning_descriptions = []
703 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
704 added_includes):
705 description_with_path = '%s\n %s' % (path, rule_description)
706 if rule_type == Rule.DISALLOW:
707 error_descriptions.append(description_with_path)
708 else:
709 warning_descriptions.append(description_with_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37710
Jeremy Leconte3d476f22023-10-17 13:04:24711 results = []
712 if error_descriptions:
713 results.append(
714 output_api.PresubmitError(
715 'You added one or more #includes that violate checkdeps rules.'
716 '\nCheck that the DEPS files in these locations contain valid '
717 'rules.\nSee '
718 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
719 'for more details about checkdeps.', error_descriptions))
720 if warning_descriptions:
721 results.append(
722 output_api.PresubmitPromptOrNotify(
723 'You added one or more #includes of files that are temporarily'
724 '\nallowed but being removed. Can you avoid introducing the\n'
725 '#include? See relevant DEPS file(s) for details and contacts.'
726 '\nSee '
727 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
728 'for more details about checkdeps.', warning_descriptions))
729 return results
kjellander@webrtc.org3bd41562014-09-01 11:06:37730
Artem Titove92675b2018-05-22 08:21:27731
charujain9893e252017-09-14 11:33:22732def CheckCommitMessageBugEntry(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24733 """Check that bug entries are well-formed in commit message."""
734 bogus_bug_msg = (
735 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
736 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.'
737 )
738 results = []
739 for bug in input_api.change.BugsFromDescription():
740 bug = bug.strip()
741 if bug.lower() == 'none':
742 continue
743 if 'b/' not in bug and ':' not in bug:
744 try:
745 if int(bug) > 100000:
746 # Rough indicator for current chromium bugs.
747 prefix_guess = 'chromium'
748 else:
749 prefix_guess = 'webrtc'
750 results.append(
751 'Bug entry requires issue tracker prefix, e.g. %s:%s' %
752 (prefix_guess, bug))
753 except ValueError:
754 results.append(bogus_bug_msg % bug)
755 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
756 results.append(bogus_bug_msg % bug)
757 return [output_api.PresubmitError(r) for r in results]
charujain9893e252017-09-14 11:33:22758
Artem Titove92675b2018-05-22 08:21:27759
charujain9893e252017-09-14 11:33:22760def CheckChangeHasBugField(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24761 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 15:11:16762
763 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 13:12:35764 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 15:11:16765 a bug reference.
Mirko Bonadei61880182017-10-12 13:12:35766
767 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
768 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 15:11:16769 """
Jeremy Leconte3d476f22023-10-17 13:04:24770 if input_api.change.BugsFromDescription():
771 return []
772 return [
773 output_api.PresubmitError(
774 'The "Bug: [bug number]" footer is mandatory. Please create a '
775 'bug and reference it using either of:\n'
776 ' * https://bugs.webrtc.org - reference it using Bug: '
777 'webrtc:XXXX\n'
778 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')
779 ]
kjellander@webrtc.orge4158642014-08-06 09:11:18780
Artem Titove92675b2018-05-22 08:21:27781
Artem Titova04d1402018-05-11 09:23:00782def CheckJSONParseErrors(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24783 """Check that JSON files do not contain syntax errors."""
784 def FilterFile(affected_file):
785 return (input_api.os_path.splitext(affected_file.LocalPath())[1]
786 == '.json' and source_file_filter(affected_file))
kjellander569cf942016-02-11 13:02:59787
Jeremy Leconte3d476f22023-10-17 13:04:24788 def GetJSONParseError(input_api, filename):
789 try:
790 contents = input_api.ReadFile(filename)
791 input_api.json.loads(contents)
792 except ValueError as e:
793 return e
794 return None
kjellander569cf942016-02-11 13:02:59795
Jeremy Leconte3d476f22023-10-17 13:04:24796 results = []
797 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
798 include_deletes=False):
799 parse_error = GetJSONParseError(input_api,
800 affected_file.AbsoluteLocalPath())
801 if parse_error:
802 results.append(
803 output_api.PresubmitError(
804 '%s could not be parsed: %s' %
805 (affected_file.LocalPath(), parse_error)))
806 return results
kjellander569cf942016-02-11 13:02:59807
808
charujain9893e252017-09-14 11:33:22809def RunPythonTests(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24810 def Join(*args):
811 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
Henrik Kjellander8d3ad822015-05-26 17:52:05812
Jeremy Leconte3d476f22023-10-17 13:04:24813 excluded_files = [
814 # These tests should be run manually after webrtc_dashboard_upload
815 # target has been built.
816 'catapult_uploader_test.py',
817 'process_perf_results_test.py',
818 ]
Christoffer Jansson70098a82022-02-21 18:43:36819
Jeremy Leconte3d476f22023-10-17 13:04:24820 test_directories = [
821 input_api.PresubmitLocalPath(),
822 Join('rtc_tools', 'py_event_log_analyzer'),
823 ] + [
824 root for root, _, files in os.walk(Join('tools_webrtc')) if any(
825 f.endswith('_test.py') and f not in excluded_files for f in files)
826 ]
Henrik Kjellander8d3ad822015-05-26 17:52:05827
Jeremy Leconte3d476f22023-10-17 13:04:24828 tests = []
Christoffer Jansson1b083a92022-02-15 13:52:31829
Jeremy Leconte3d476f22023-10-17 13:04:24830 for directory in test_directories:
831 tests.extend(
832 input_api.canned_checks.GetUnitTestsInDirectory(
833 input_api,
834 output_api,
835 directory,
836 files_to_check=[r'.+_test\.py$'],
837 run_on_python2=False))
838 return input_api.RunTests(tests, parallel=True)
Henrik Kjellander8d3ad822015-05-26 17:52:05839
840
Artem Titova04d1402018-05-11 09:23:00841def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
842 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24843 """Checks that the namespace google::protobuf has not been used."""
Dor Hen59d592e2024-09-16 12:48:45844 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24845 pattern = input_api.re.compile(r'google::protobuf')
846 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
847 file_filter = lambda x: (input_api.FilterSourceFile(x) and
848 source_file_filter(x))
849 for f in input_api.AffectedSourceFiles(file_filter):
850 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
851 continue
852 contents = input_api.ReadFile(f)
853 if pattern.search(contents):
Dor Hen59d592e2024-09-16 12:48:45854 files.add(f)
mbonadei38415b22017-04-07 12:38:01855
Jeremy Leconte3d476f22023-10-17 13:04:24856 if files:
857 return [
858 output_api.PresubmitError(
859 'Please avoid to use namespace `google::protobuf` directly.\n'
860 'Add a using directive in `%s` and include that header instead.'
Dor Hen59d592e2024-09-16 12:48:45861 % proto_utils_path, list(files))
Jeremy Leconte3d476f22023-10-17 13:04:24862 ]
863 return []
mbonadei38415b22017-04-07 12:38:01864
865
Mirko Bonadei92ea95e2017-09-15 04:47:31866def _LicenseHeader(input_api):
Jeremy Leconte3d476f22023-10-17 13:04:24867 """Returns the license header regexp."""
868 # Accept any year number from 2003 to the current year
869 current_year = int(input_api.time.strftime('%Y'))
870 allowed_years = (str(s) for s in reversed(range(2003, current_year + 1)))
871 years_re = '(' + '|'.join(allowed_years) + ')'
872 license_header = (
873 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
874 r'All [Rr]ights [Rr]eserved\.\n'
875 r'.*?\n'
876 r'.*? Use of this source code is governed by a BSD-style license\n'
877 r'.*? that can be found in the LICENSE file in the root of the source\n'
878 r'.*? tree\. An additional intellectual property rights grant can be '
879 r'found\n'
880 r'.*? in the file PATENTS\. All contributing project authors may\n'
881 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
882 ) % {
883 'year': years_re,
884 }
885 return license_header
Mirko Bonadei92ea95e2017-09-15 04:47:31886
887
charujain9893e252017-09-14 11:33:22888def CommonChecks(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24889 """Checks common to both upload and commit."""
890 results = []
891 # Filter out files that are in objc or ios dirs from being cpplint-ed since
892 # they do not follow C++ lint rules.
893 exception_list = input_api.DEFAULT_FILES_TO_SKIP + (
894 r".*\bobjc[\\\/].*",
895 r".*objc\.[hcm]+$",
896 )
897 source_file_filter = lambda x: input_api.FilterSourceFile(
898 x, None, exception_list)
899 results.extend(
900 CheckApprovedFilesLintClean(input_api, output_api, source_file_filter))
901 results.extend(
902 input_api.canned_checks.CheckLicense(input_api, output_api,
903 _LicenseHeader(input_api)))
Byoungchan Lee94f2ef22021-07-01 13:21:44904
Jeremy Leconte3d476f22023-10-17 13:04:24905 # TODO(bugs.webrtc.org/12114): Delete this filter and run pylint on
906 # all python files. This is a temporary solution.
907 python_file_filter = lambda f: (f.LocalPath().endswith('.py') and
908 source_file_filter(f))
909 python_changed_files = [
910 f.LocalPath()
911 for f in input_api.AffectedFiles(include_deletes=False,
912 file_filter=python_file_filter)
913 ]
914 pylint_new_style = [
915 f for f in python_changed_files if f not in PYLINT_OLD_STYLE
916 ]
917 pylint_old_style = [
918 f for f in python_changed_files if f in PYLINT_OLD_STYLE
919 ]
920 if pylint_new_style:
921 results.extend(
922 input_api.canned_checks.RunPylint(
923 input_api,
924 output_api,
925 files_to_check=pylint_new_style,
926 files_to_skip=(
927 r'^base[\\\/].*\.py$',
928 r'^build[\\\/].*\.py$',
929 r'^buildtools[\\\/].*\.py$',
930 r'^infra[\\\/].*\.py$',
931 r'^ios[\\\/].*\.py$',
932 r'^out.*[\\\/].*\.py$',
933 r'^testing[\\\/].*\.py$',
934 r'^third_party[\\\/].*\.py$',
935 r'^tools[\\\/].*\.py$',
936 r'^xcodebuild.*[\\\/].*\.py$',
937 ),
938 pylintrc='pylintrc',
939 version='2.7'))
Byoungchan Lee94f2ef22021-07-01 13:21:44940
Jeremy Leconte3d476f22023-10-17 13:04:24941 if pylint_old_style:
942 results.extend(
943 input_api.canned_checks.RunPylint(input_api,
944 output_api,
945 files_to_check=pylint_old_style,
946 pylintrc='pylintrc_old_style',
947 version='2.7'))
948 # TODO(bugs.webrtc.org/13606): talk/ is no more, so make below checks
949 # simpler. WebRTC can't use the presubmit_canned_checks.PanProjectChecks
950 # function since we need to have different license checks in talk/ and
951 # webrtc/directories. Instead, hand-picked checks are included below.
kjellander569cf942016-02-11 13:02:59952
Jeremy Leconte3d476f22023-10-17 13:04:24953 # .m and .mm files are ObjC files. For simplicity we will consider
954 # .h files in ObjC subdirectories ObjC headers.
955 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
956 # Skip long-lines check for DEPS and GN files.
957 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
958 # Also we will skip most checks for third_party directory.
959 third_party_filter_list = (r'(^|.*[\\\/])third_party[\\\/].+', )
960 eighty_char_sources = lambda x: input_api.FilterSourceFile(
961 x,
962 files_to_skip=build_file_filter_list + objc_filter_list +
963 third_party_filter_list)
964 hundred_char_sources = lambda x: input_api.FilterSourceFile(
965 x, files_to_check=objc_filter_list)
966 non_third_party_sources = lambda x: input_api.FilterSourceFile(
967 x, files_to_skip=third_party_filter_list)
Henrik Kjellander63224672015-09-08 06:03:56968
Jeremy Leconte3d476f22023-10-17 13:04:24969 results.extend(
970 input_api.canned_checks.CheckLongLines(
971 input_api,
972 output_api,
973 maxlen=80,
974 source_file_filter=eighty_char_sources))
975 results.extend(
976 input_api.canned_checks.CheckLongLines(
977 input_api,
978 output_api,
979 maxlen=100,
980 source_file_filter=hundred_char_sources))
981 results.extend(
982 input_api.canned_checks.CheckChangeHasNoTabs(
983 input_api, output_api, source_file_filter=non_third_party_sources))
984 results.extend(
985 input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
986 input_api, output_api, source_file_filter=non_third_party_sources))
987 results.extend(
988 input_api.canned_checks.CheckAuthorizedAuthor(
989 input_api,
990 output_api,
991 bot_allowlist=[
992 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com',
993 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com',
994 ]))
995 results.extend(
996 input_api.canned_checks.CheckChangeTodoHasOwner(
997 input_api, output_api, source_file_filter=non_third_party_sources))
998 results.extend(
999 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
1000 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
1001 results.extend(
1002 CheckNoIOStreamInHeaders(input_api,
1003 output_api,
1004 source_file_filter=non_third_party_sources))
1005 results.extend(
1006 CheckNoPragmaOnce(input_api,
1007 output_api,
1008 source_file_filter=non_third_party_sources))
1009 results.extend(
1010 CheckNoFRIEND_TEST(input_api,
Mirko Bonadei8cc66952020-10-30 09:13:451011 output_api,
1012 source_file_filter=non_third_party_sources))
Jeremy Leconte3d476f22023-10-17 13:04:241013 results.extend(CheckGnChanges(input_api, output_api))
1014 results.extend(
1015 CheckUnwantedDependencies(input_api,
1016 output_api,
1017 source_file_filter=non_third_party_sources))
1018 results.extend(
1019 CheckJSONParseErrors(input_api,
1020 output_api,
1021 source_file_filter=non_third_party_sources))
1022 results.extend(RunPythonTests(input_api, output_api))
1023 results.extend(
1024 CheckUsageOfGoogleProtobufNamespace(
1025 input_api, output_api, source_file_filter=non_third_party_sources))
1026 results.extend(
1027 CheckOrphanHeaders(input_api,
1028 output_api,
1029 source_file_filter=non_third_party_sources))
1030 results.extend(
1031 CheckNewlineAtTheEndOfProtoFiles(
1032 input_api, output_api, source_file_filter=non_third_party_sources))
1033 results.extend(
Byoungchan Leeeb76f192024-01-22 16:55:141034 CheckLFNewline(input_api, output_api, non_third_party_sources))
1035 results.extend(
Jeremy Leconte3d476f22023-10-17 13:04:241036 CheckNoStreamUsageIsAdded(input_api, output_api,
Mirko Bonadei8cc66952020-10-30 09:13:451037 non_third_party_sources))
Jeremy Leconte3d476f22023-10-17 13:04:241038 results.extend(
1039 CheckNoTestCaseUsageIsAdded(input_api, output_api,
1040 non_third_party_sources))
1041 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1042 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
1043 results.extend(
1044 CheckAbslMemoryInclude(input_api, output_api, non_third_party_sources))
1045 results.extend(
1046 CheckAssertUsage(input_api, output_api, non_third_party_sources))
1047 results.extend(
1048 CheckBannedAbslMakeUnique(input_api, output_api,
1049 non_third_party_sources))
1050 results.extend(
Florent Castelli9212f092024-08-29 13:42:571051 CheckBannedAbslOptional(input_api, output_api,
1052 non_third_party_sources))
1053 results.extend(
Jeremy Leconte3d476f22023-10-17 13:04:241054 CheckObjcApiSymbols(input_api, output_api, non_third_party_sources))
Jeremy Leconte83d1f9a2024-09-10 07:47:491055 results.extend(
Jeremy Leconte1bd331f2024-09-11 02:48:411056 CheckConditionalIncludes(input_api, output_api,
1057 non_third_party_sources))
Jeremy Leconte3d476f22023-10-17 13:04:241058 return results
Mirko Bonadeia418e672018-10-24 11:57:251059
1060
1061def CheckApiDepsFileIsUpToDate(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241062 """Check that 'include_rules' in api/DEPS is up to date.
Mirko Bonadei90490372018-10-26 11:17:471063
1064 The file api/DEPS must be kept up to date in order to avoid to avoid to
1065 include internal header from WebRTC's api/ headers.
1066
1067 This check is focused on ensuring that 'include_rules' contains a deny
1068 rule for each root level directory. More focused allow rules can be
1069 added to 'specific_include_rules'.
1070 """
Jeremy Leconte3d476f22023-10-17 13:04:241071 results = []
1072 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
1073 with open(api_deps) as f:
1074 deps_content = _ParseDeps(f.read())
Mirko Bonadeia418e672018-10-24 11:57:251075
Jeremy Leconte3d476f22023-10-17 13:04:241076 include_rules = deps_content.get('include_rules', [])
1077 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 11:57:251078
Jeremy Leconte3d476f22023-10-17 13:04:241079 # Only check top level directories affected by the current CL.
1080 dirs_to_check = set()
1081 for f in input_api.AffectedFiles():
1082 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
1083 if len(path_tokens) > 1:
1084 if (path_tokens[0] not in dirs_to_skip and os.path.isdir(
1085 os.path.join(input_api.PresubmitLocalPath(),
1086 path_tokens[0]))):
1087 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 11:57:251088
Jeremy Leconte3d476f22023-10-17 13:04:241089 missing_include_rules = set()
1090 for p in dirs_to_check:
1091 rule = '-%s' % p
1092 if rule not in include_rules:
1093 missing_include_rules.add(rule)
Mirko Bonadei90490372018-10-26 11:17:471094
Jeremy Leconte3d476f22023-10-17 13:04:241095 if missing_include_rules:
1096 error_msg = [
1097 'include_rules = [\n',
1098 ' ...\n',
1099 ]
Mirko Bonadeia418e672018-10-24 11:57:251100
Jeremy Leconte3d476f22023-10-17 13:04:241101 for r in sorted(missing_include_rules):
1102 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 11:57:251103
Jeremy Leconte3d476f22023-10-17 13:04:241104 error_msg.append(' ...\n')
1105 error_msg.append(']\n')
Mirko Bonadei90490372018-10-26 11:17:471106
Jeremy Leconte3d476f22023-10-17 13:04:241107 results.append(
1108 output_api.PresubmitError(
1109 'New root level directory detected! WebRTC api/ headers should '
1110 'not #include headers from \n'
1111 'the new directory, so please update "include_rules" in file\n'
1112 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
Mirko Bonadei90490372018-10-26 11:17:471113
Jeremy Leconte3d476f22023-10-17 13:04:241114 return results
Mirko Bonadei8cc66952020-10-30 09:13:451115
andrew@webrtc.org2442de12012-01-23 17:45:411116
Mirko Bonadei9fa8ef12019-09-17 17:14:131117def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241118 file_filter = lambda f: (f.LocalPath().endswith(
1119 ('.cc', '.h')) and source_file_filter(f))
Mirko Bonadei9fa8ef12019-09-17 17:14:131120
Dor Hen59d592e2024-09-16 12:48:451121 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:241122 for f in input_api.AffectedFiles(include_deletes=False,
1123 file_filter=file_filter):
1124 for _, line in f.ChangedContents():
1125 if 'absl::make_unique' in line:
Dor Hen59d592e2024-09-16 12:48:451126 files.add(f)
Jeremy Leconte3d476f22023-10-17 13:04:241127 break
Mirko Bonadei9fa8ef12019-09-17 17:14:131128
Jeremy Leconte3d476f22023-10-17 13:04:241129 if files:
1130 return [
1131 output_api.PresubmitError(
1132 'Please use std::make_unique instead of absl::make_unique.\n'
Dor Hen59d592e2024-09-16 12:48:451133 'Affected files:', list(files))
Jeremy Leconte3d476f22023-10-17 13:04:241134 ]
1135 return []
Mirko Bonadei8cc66952020-10-30 09:13:451136
Mirko Bonadei9fa8ef12019-09-17 17:14:131137
Florent Castelli9212f092024-08-29 13:42:571138def CheckBannedAbslOptional(input_api, output_api, source_file_filter):
1139 absl_optional = re.compile(r'absl::(optional|make_optional|nullopt)',
1140 re.MULTILINE)
1141 absl_optional_include = re.compile(r'^#include\s*"absl/types/optional\.h"',
1142 input_api.re.MULTILINE)
1143 file_filter = lambda f: (f.LocalPath().endswith(
1144 ('.cc', '.h')) and source_file_filter(f))
1145
Dor Hen59d592e2024-09-16 12:48:451146 files = set()
Florent Castelli9212f092024-08-29 13:42:571147 for f in input_api.AffectedFiles(include_deletes=False,
1148 file_filter=file_filter):
1149 for _, line in f.ChangedContents():
1150 if absl_optional.search(line) or absl_optional_include.search(
1151 line):
Dor Hen59d592e2024-09-16 12:48:451152 files.add(f.LocalPath())
Florent Castelli9212f092024-08-29 13:42:571153 break
1154
1155 if files:
1156 return [
1157 output_api.PresubmitError(
1158 'Please use std::optional instead of absl::optional.\n'
Dor Hen59d592e2024-09-16 12:48:451159 'Affected files:', list(files))
Florent Castelli9212f092024-08-29 13:42:571160 ]
1161 return []
1162
1163
Jeremy Leconte1bd331f2024-09-11 02:48:411164def CheckConditionalIncludes(input_api, output_api, source_file_filter):
1165 conditional_includes = {
1166 '<netinet/in.h>': '"rtc_base/ip_address.h"',
1167 '<sys/socket.h>': '"rtc_base/net_helpers.h"',
1168 }
Jeremy Leconte83d1f9a2024-09-10 07:47:491169 file_filter = lambda f: (f.LocalPath().endswith(
1170 ('.cc', '.h')) and source_file_filter(f))
Jeremy Leconte1bd331f2024-09-11 02:48:411171 results = []
1172 for key, value in conditional_includes.items():
1173 include_regex = re.compile('^#include ' + key +
1174 '((?!IWYU pragma|no-presubmit-check).)*$')
Dor Hen59d592e2024-09-16 12:48:451175 files = set()
Jeremy Leconte1bd331f2024-09-11 02:48:411176 for f in input_api.AffectedFiles(include_deletes=False,
1177 file_filter=file_filter):
1178 for _, line in f.ChangedContents():
1179 if include_regex.search(line):
Dor Hen59d592e2024-09-16 12:48:451180 files.add(f.LocalPath())
Jeremy Leconte1bd331f2024-09-11 02:48:411181 break
Jeremy Leconte83d1f9a2024-09-10 07:47:491182
Jeremy Leconte1bd331f2024-09-11 02:48:411183 if files:
1184 results.append(
1185 output_api.PresubmitError(
1186 'Please include ' + value + ' instead of ' + key +
Dor Hen59d592e2024-09-16 12:48:451187 '.\nAffected files:', list(files)))
Jeremy Leconte1bd331f2024-09-11 02:48:411188 return results
Jeremy Leconte83d1f9a2024-09-10 07:47:491189
1190
Mirko Bonadeid74c0e62020-07-16 19:57:011191def CheckObjcApiSymbols(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241192 rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}',
1193 re.MULTILINE | re.DOTALL)
1194 file_filter = lambda f: (f.LocalPath().endswith(
1195 ('.h')) and source_file_filter(f))
Mirko Bonadeid74c0e62020-07-16 19:57:011196
Dor Hen59d592e2024-09-16 12:48:451197 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:241198 file_filter = lambda x: (input_api.FilterSourceFile(x) and
1199 source_file_filter(x))
1200 for f in input_api.AffectedSourceFiles(file_filter):
1201 if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath():
1202 continue
1203 if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'):
1204 continue
1205 contents = input_api.ReadFile(f)
1206 for match in rtc_objc_export.finditer(contents):
1207 export_block = match.group(0)
1208 if 'RTC_OBJC_TYPE' not in export_block:
Dor Hen59d592e2024-09-16 12:48:451209 files.add(f.LocalPath())
Mirko Bonadeid74c0e62020-07-16 19:57:011210
Jeremy Leconte3d476f22023-10-17 13:04:241211 if len(files) > 0:
1212 return [
1213 output_api.PresubmitError(
1214 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() '
1215 + 'macro.\n\n' + 'For example:\n' +
1216 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' +
1217 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' +
Dor Hen59d592e2024-09-16 12:48:451218 'Please fix the following files:', list(files))
Jeremy Leconte3d476f22023-10-17 13:04:241219 ]
1220 return []
Mirko Bonadei8cc66952020-10-30 09:13:451221
Mirko Bonadeid74c0e62020-07-16 19:57:011222
Mirko Bonadeia6395132021-07-22 15:35:591223def CheckAssertUsage(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241224 pattern = input_api.re.compile(r'\bassert\(')
1225 file_filter = lambda f: (f.LocalPath().endswith(
1226 ('.cc', '.h', '.m', '.mm')) and source_file_filter(f))
Mirko Bonadeia6395132021-07-22 15:35:591227
Dor Hen59d592e2024-09-16 12:48:451228 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:241229 for f in input_api.AffectedFiles(include_deletes=False,
1230 file_filter=file_filter):
1231 for _, line in f.ChangedContents():
1232 if pattern.search(line):
Dor Hen59d592e2024-09-16 12:48:451233 files.add(f.LocalPath())
Jeremy Leconte3d476f22023-10-17 13:04:241234 break
Mirko Bonadeia6395132021-07-22 15:35:591235
Jeremy Leconte3d476f22023-10-17 13:04:241236 if len(files) > 0:
1237 return [
1238 output_api.PresubmitError(
1239 'Usage of assert() has been detected in the following files, '
Dor Hen59d592e2024-09-16 12:48:451240 'please use RTC_DCHECK() instead.\n Files:', list(files))
Jeremy Leconte3d476f22023-10-17 13:04:241241 ]
1242 return []
Mirko Bonadeia6395132021-07-22 15:35:591243
1244
tzika06bf852018-11-15 11:37:351245def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241246 pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"',
1247 input_api.re.MULTILINE)
1248 file_filter = lambda f: (f.LocalPath().endswith(
1249 ('.cc', '.h')) and source_file_filter(f))
tzika06bf852018-11-15 11:37:351250
Dor Hen59d592e2024-09-16 12:48:451251 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:241252 for f in input_api.AffectedFiles(include_deletes=False,
1253 file_filter=file_filter):
1254 contents = input_api.ReadFile(f)
1255 if pattern.search(contents):
1256 continue
1257 for _, line in f.ChangedContents():
1258 if 'absl::WrapUnique' in line:
Dor Hen59d592e2024-09-16 12:48:451259 files.add(f)
Jeremy Leconte3d476f22023-10-17 13:04:241260 break
tzika06bf852018-11-15 11:37:351261
Jeremy Leconte3d476f22023-10-17 13:04:241262 if len(files) > 0:
1263 return [
1264 output_api.PresubmitError(
1265 'Please include "absl/memory/memory.h" header for '
1266 'absl::WrapUnique.\nThis header may or may not be included '
Dor Hen59d592e2024-09-16 12:48:451267 'transitively depending on the C++ standard version.',
1268 list(files))
Jeremy Leconte3d476f22023-10-17 13:04:241269 ]
1270 return []
Mirko Bonadei8cc66952020-10-30 09:13:451271
kjellander@webrtc.orge4158642014-08-06 09:11:181272
andrew@webrtc.org53df1362012-01-26 21:24:231273def CheckChangeOnUpload(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241274 results = []
1275 results.extend(CommonChecks(input_api, output_api))
1276 results.extend(CheckGnGen(input_api, output_api))
1277 results.extend(
1278 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
1279 return results
niklase@google.comda159d62011-05-30 11:51:341280
kjellander@webrtc.orge4158642014-08-06 09:11:181281
andrew@webrtc.org2442de12012-01-23 17:45:411282def CheckChangeOnCommit(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241283 results = []
1284 results.extend(CommonChecks(input_api, output_api))
1285 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
1286 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
1287 results.extend(
1288 input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api))
1289 results.extend(
1290 input_api.canned_checks.CheckChangeHasDescription(
1291 input_api, output_api))
1292 results.extend(CheckChangeHasBugField(input_api, output_api))
1293 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
1294 results.extend(
1295 input_api.canned_checks.CheckTreeIsOpen(
1296 input_api,
1297 output_api,
1298 json_url='http://webrtc-status.appspot.com/current?format=json'))
1299 return results
mbonadei74973ed2017-05-09 14:58:051300
1301
Artem Titova04d1402018-05-11 09:23:001302def CheckOrphanHeaders(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241303 # We need to wait until we have an input_api object and use this
1304 # roundabout construct to import prebubmit_checks_lib because this file is
1305 # eval-ed and thus doesn't have __file__.
1306 error_msg = """{} should be listed in {}."""
1307 results = []
1308 exempt_paths = [re.escape(os.path.join('tools_webrtc', 'ios', 'SDK'))]
Christoffer Jansson884e8ae2022-02-11 20:29:381309
Jeremy Leconte3d476f22023-10-17 13:04:241310 with _AddToPath(
1311 input_api.os_path.join(input_api.PresubmitLocalPath(),
1312 'tools_webrtc', 'presubmit_checks_lib')):
1313 from check_orphan_headers import GetBuildGnPathFromFilePath
1314 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 14:58:051315
Jeremy Leconte3d476f22023-10-17 13:04:241316 file_filter = lambda x: input_api.FilterSourceFile(
1317 x, files_to_skip=exempt_paths) and source_file_filter(x)
1318 for f in input_api.AffectedSourceFiles(file_filter):
1319 if f.LocalPath().endswith('.h'):
1320 file_path = os.path.abspath(f.LocalPath())
1321 root_dir = os.getcwd()
1322 gn_file_path = GetBuildGnPathFromFilePath(file_path,
1323 os.path.exists, root_dir)
1324 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1325 if not in_build_gn:
1326 results.append(
1327 output_api.PresubmitError(
1328 error_msg.format(f.LocalPath(),
1329 os.path.relpath(gn_file_path))))
1330 return results
Mirko Bonadei960fd5b2017-06-29 12:59:361331
1332
Mirko Bonadei8cc66952020-10-30 09:13:451333def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api,
1334 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241335 """Checks that all .proto files are terminated with a newline."""
1336 error_msg = 'File {} must end with exactly one newline.'
1337 results = []
1338 file_filter = lambda x: input_api.FilterSourceFile(
1339 x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x)
1340 for f in input_api.AffectedSourceFiles(file_filter):
1341 file_path = f.LocalPath()
1342 with open(file_path) as f:
1343 lines = f.readlines()
1344 if len(lines) > 0 and not lines[-1].endswith('\n'):
1345 results.append(
1346 output_api.PresubmitError(error_msg.format(file_path)))
1347 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231348
1349
Byoungchan Leeeb76f192024-01-22 16:55:141350def CheckLFNewline(input_api, output_api, source_file_filter):
1351 """Checks that all files have LF newlines."""
1352 error_msg = 'File {} must use LF newlines.'
1353 results = []
1354 file_filter = lambda x: input_api.FilterSourceFile(
1355 x, files_to_check=(r'.+', )) and source_file_filter(x)
1356 for f in input_api.AffectedSourceFiles(file_filter):
1357 file_path = f.LocalPath()
1358 with open(file_path, 'rb') as f:
1359 if b'\r\n' in f.read():
1360 results.append(
1361 output_api.PresubmitError(error_msg.format(file_path)))
1362 return results
1363
Mirko Bonadei7e4ee6e2018-09-28 09:45:231364def _ExtractAddRulesFromParsedDeps(parsed_deps):
Jeremy Leconte3d476f22023-10-17 13:04:241365 """Extract the rules that add dependencies from a parsed DEPS file.
Mirko Bonadei7e4ee6e2018-09-28 09:45:231366
1367 Args:
1368 parsed_deps: the locals dictionary from evaluating the DEPS file."""
Jeremy Leconte3d476f22023-10-17 13:04:241369 add_rules = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231370 add_rules.update([
Jeremy Leconte3d476f22023-10-17 13:04:241371 rule[1:] for rule in parsed_deps.get('include_rules', [])
Mirko Bonadei7e4ee6e2018-09-28 09:45:231372 if rule.startswith('+') or rule.startswith('!')
1373 ])
Jeremy Leconte3d476f22023-10-17 13:04:241374 for _, rules in parsed_deps.get('specific_include_rules', {}).items():
1375 add_rules.update([
1376 rule[1:] for rule in rules
1377 if rule.startswith('+') or rule.startswith('!')
1378 ])
1379 return add_rules
Mirko Bonadei7e4ee6e2018-09-28 09:45:231380
1381
1382def _ParseDeps(contents):
Jeremy Leconte3d476f22023-10-17 13:04:241383 """Simple helper for parsing DEPS files."""
Mirko Bonadei7e4ee6e2018-09-28 09:45:231384
Jeremy Leconte3d476f22023-10-17 13:04:241385 # Stubs for handling special syntax in the root DEPS file.
1386 class VarImpl:
1387 def __init__(self, local_scope):
1388 self._local_scope = local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231389
Jeremy Leconte3d476f22023-10-17 13:04:241390 def Lookup(self, var_name):
1391 """Implements the Var syntax."""
1392 try:
1393 return self._local_scope['vars'][var_name]
1394 except KeyError as var_not_defined:
1395 raise Exception('Var is not defined: %s' %
1396 var_name) from var_not_defined
Mirko Bonadei7e4ee6e2018-09-28 09:45:231397
Jeremy Leconte3d476f22023-10-17 13:04:241398 local_scope = {}
1399 global_scope = {
1400 'Var': VarImpl(local_scope).Lookup,
1401 }
1402 exec(contents, global_scope, local_scope)
1403 return local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231404
1405
1406def _CalculateAddedDeps(os_path, old_contents, new_contents):
Jeremy Leconte3d476f22023-10-17 13:04:241407 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
Mirko Bonadei7e4ee6e2018-09-28 09:45:231408 a set of DEPS entries that we should look up.
1409
1410 For a directory (rather than a specific filename) we fake a path to
1411 a specific filename by adding /DEPS. This is chosen as a file that
1412 will seldom or never be subject to per-file include_rules.
1413 """
Jeremy Leconte3d476f22023-10-17 13:04:241414 # We ignore deps entries on auto-generated directories.
1415 auto_generated_dirs = ['grit', 'jni']
Mirko Bonadei7e4ee6e2018-09-28 09:45:231416
Jeremy Leconte3d476f22023-10-17 13:04:241417 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1418 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231419
Jeremy Leconte3d476f22023-10-17 13:04:241420 added_deps = new_deps.difference(old_deps)
Mirko Bonadei7e4ee6e2018-09-28 09:45:231421
Jeremy Leconte3d476f22023-10-17 13:04:241422 results = set()
1423 for added_dep in added_deps:
1424 if added_dep.split('/')[0] in auto_generated_dirs:
1425 continue
1426 # Assume that a rule that ends in .h is a rule for a specific file.
1427 if added_dep.endswith('.h'):
1428 results.add(added_dep)
1429 else:
1430 results.add(os_path.join(added_dep, 'DEPS'))
1431 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231432
1433
1434def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241435 """When a dependency prefixed with + is added to a DEPS file, we
Edward Lesmesbef08502021-02-05 22:08:321436 want to make sure that the change is reviewed by an OWNER of the
1437 target file or directory, to avoid layering violations from being
1438 introduced. This check verifies that this happens.
1439 """
Jeremy Leconte3d476f22023-10-17 13:04:241440 virtual_depended_on_files = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231441
Jeremy Leconte3d476f22023-10-17 13:04:241442 file_filter = lambda f: not input_api.re.match(
1443 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1444 for f in input_api.AffectedFiles(include_deletes=False,
1445 file_filter=file_filter):
1446 filename = input_api.os_path.basename(f.LocalPath())
1447 if filename == 'DEPS':
1448 virtual_depended_on_files.update(
1449 _CalculateAddedDeps(input_api.os_path,
1450 '\n'.join(f.OldContents()),
1451 '\n'.join(f.NewContents())))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231452
Jeremy Leconte3d476f22023-10-17 13:04:241453 if not virtual_depended_on_files:
1454 return []
Christoffer Jansson4e8a7732022-02-08 08:01:121455
Jeremy Leconte3d476f22023-10-17 13:04:241456 if input_api.is_committing:
1457 if input_api.tbr:
1458 return [
1459 output_api.PresubmitNotifyResult(
1460 '--tbr was specified, skipping OWNERS check for DEPS '
1461 'additions')
1462 ]
1463 if input_api.dry_run:
1464 return [
1465 output_api.PresubmitNotifyResult(
1466 'This is a dry run, skipping OWNERS check for DEPS '
1467 'additions')
1468 ]
1469 if not input_api.change.issue:
1470 return [
1471 output_api.PresubmitError(
1472 "DEPS approval by OWNERS check failed: this change has "
1473 "no change number, so we can't check it for approvals.")
1474 ]
1475 output = output_api.PresubmitError
1476 else:
1477 output = output_api.PresubmitNotifyResult
Christoffer Jansson4e8a7732022-02-08 08:01:121478
Jeremy Leconte3d476f22023-10-17 13:04:241479 owner_email, reviewers = (
1480 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1481 input_api, None, approval_needed=input_api.is_committing))
Christoffer Jansson4e8a7732022-02-08 08:01:121482
Jeremy Leconte3d476f22023-10-17 13:04:241483 owner_email = owner_email or input_api.change.author_email
Christoffer Jansson4e8a7732022-02-08 08:01:121484
Jeremy Leconte3d476f22023-10-17 13:04:241485 approval_status = input_api.owners_client.GetFilesApprovalStatus(
1486 virtual_depended_on_files, reviewers.union([owner_email]), [])
1487 missing_files = [
1488 f for f in virtual_depended_on_files
1489 if approval_status[f] != input_api.owners_client.APPROVED
Christoffer Jansson4e8a7732022-02-08 08:01:121490 ]
Christoffer Jansson4e8a7732022-02-08 08:01:121491
Jeremy Leconte3d476f22023-10-17 13:04:241492 # We strip the /DEPS part that was added by
1493 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1494 # directory.
1495 def StripDeps(path):
1496 start_deps = path.rfind('/DEPS')
1497 if start_deps != -1:
1498 return path[:start_deps]
1499 return path
1500
1501 unapproved_dependencies = [
1502 "'+%s'," % StripDeps(path) for path in missing_files
1503 ]
1504
1505 if unapproved_dependencies:
1506 output_list = [
1507 output(
1508 'You need LGTM from owners of depends-on paths in DEPS that '
1509 ' were modified in this CL:\n %s' %
1510 '\n '.join(sorted(unapproved_dependencies)))
1511 ]
1512 suggested_owners = input_api.owners_client.SuggestOwners(
1513 missing_files, exclude=[owner_email])
1514 output_list.append(
1515 output('Suggested missing target path OWNERS:\n %s' %
1516 '\n '.join(suggested_owners or [])))
1517 return output_list
1518
1519 return []