Thanks to visit codestin.com
Credit goes to webrtc.googlesource.com

blob: 153d77302bc7425e60acbb9ff66a26fd22b7c779 [file] [log] [blame]
Christoffer Jansson4e8a7732022-02-08 08:01:121#!/usr/bin/env vpython3
2
[email protected]2442de12012-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.
[email protected]da159d62011-05-30 11:51:3410
kjellander7439f972016-12-06 06:47:4611import json
[email protected]aefe61a2014-12-08 13:00:3012import os
[email protected]85759802013-10-22 16:47:4013import re
[email protected]3bd41562014-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
[email protected]85759802013-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',
Christoffer Jansson4e8a7732022-02-08 08:01:1236 'PRESUBMIT.py',
37 'presubmit_test_mocks.py',
38 'presubmit_test.py',
Mirko Bonadei8cc66952020-10-30 09:13:4539 'rtc_base',
40 'sdk/android/src/jni',
41 'sdk/objc',
42 'system_wrappers',
43 'test',
44 'tools_webrtc',
45 'voice_engine',
[email protected]0fcaf992015-11-26 14:24:5246]
47
Jeremy Leconte3d476f22023-10-17 13:04:2448PYLINT_OLD_STYLE = [
49 "PRESUBMIT.py",
Jeremy Leconte908c21c2023-10-17 14:02:5450 "tools_webrtc/autoroller/roll_deps.py",
Christoffer Dewerin81a91172024-04-08 07:26:4151 "tools_webrtc/android/build_aar.py",
52 "tools_webrtc/ios/build_ios_libs.py",
Jeremy Leconteef4d62e2024-05-16 11:27:0353 "tools_webrtc/mb/mb.py",
54 "tools_webrtc/mb/mb_unittest.py",
Jeremy Leconte3d476f22023-10-17 13:04:2455]
56
jbauchc4e3ead2016-02-19 08:25:5557# These filters will always be removed, even if the caller specifies a filter
58# set, as they are problematic or broken in some way.
59#
60# Justifications for each filter:
61# - build/c++11 : Rvalue ref checks are unreliable (false positives),
Mirko Bonadeifc17a782020-06-30 12:31:3762# include file and feature blocklists are
jbauchc4e3ead2016-02-19 08:25:5563# google3-specific.
Mirko Bonadei7b4b29a2023-11-29 16:01:3864# - readability/todo : WebRTC puts bug links, not usernames, in TODOs.
65# The new TODO style also doesn't match with this check.
Mirko Bonadeie92e2862020-05-29 13:23:0966# - runtime/references : Mutable references are not banned by the Google
67# C++ style guide anymore (starting from May 2020).
kjellandere5a87a52016-04-27 09:32:1268# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
69# all move-related errors).
Mirko Bonadeifc17a782020-06-30 12:31:3770DISABLED_LINT_FILTERS = [
Mirko Bonadei8cc66952020-10-30 09:13:4571 '-build/c++11',
Mirko Bonadei7b4b29a2023-11-29 16:01:3872 '-readability/todo',
Mirko Bonadei8cc66952020-10-30 09:13:4573 '-runtime/references',
74 '-whitespace/operators',
jbauchc4e3ead2016-02-19 08:25:5575]
76
kjellanderfd595232015-12-04 10:44:0977# List of directories of "supported" native APIs. That means changes to headers
78# will be done in a compatible way following this scheme:
79# 1. Non-breaking changes are made.
80# 2. The old APIs as marked as deprecated (with comments).
81# 3. Deprecation is announced to [email protected] and
82# [email protected] (internal list).
83# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-03 07:56:1484NATIVE_API_DIRS = (
Mirko Bonadei8cc66952020-10-30 09:13:4585 'api', # All subdirectories of api/ are included as well.
86 'media/base',
87 'media/engine',
88 'modules/audio_device/include',
89 'pc',
kjellanderdd705472016-06-09 18:17:2790)
Mirko Bonadei4dc4e252017-09-19 11:49:1691
kjellanderdd705472016-06-09 18:17:2792# These directories should not be used but are maintained only to avoid breaking
93# some legacy downstream code.
94LEGACY_API_DIRS = (
Mirko Bonadei8cc66952020-10-30 09:13:4595 'common_audio/include',
96 'modules/audio_coding/include',
97 'modules/audio_processing/include',
98 'modules/congestion_controller/include',
99 'modules/include',
100 'modules/remote_bitrate_estimator/include',
101 'modules/rtp_rtcp/include',
102 'modules/rtp_rtcp/source',
103 'modules/utility/include',
104 'modules/video_coding/codecs/h264/include',
105 'modules/video_coding/codecs/vp8/include',
106 'modules/video_coding/codecs/vp9/include',
107 'modules/video_coding/include',
108 'rtc_base',
109 'system_wrappers/include',
kjellander53047c92015-12-03 07:56:14110)
Mirko Bonadei4dc4e252017-09-19 11:49:16111
Karl Wibergd4f01c12017-11-10 09:55:45112# NOTE: The set of directories in API_DIRS should be the same as those
113# listed in the table in native-api.md.
kjellanderdd705472016-06-09 18:17:27114API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-03 07:56:14115
Mirko Bonadei4dc4e252017-09-19 11:49:16116# TARGET_RE matches a GN target, and extracts the target name and the contents.
Mirko Bonadei2dcf3482020-06-05 12:30:41117TARGET_RE = re.compile(
Mirko Bonadei8cc66952020-10-30 09:13:45118 r'(?P<indent>\s*)(?P<target_type>\w+)\("(?P<target_name>\w+)"\) {'
119 r'(?P<target_contents>.*?)'
120 r'(?P=indent)}', re.MULTILINE | re.DOTALL)
Mirko Bonadei4dc4e252017-09-19 11:49:16121
122# SOURCES_RE matches a block of sources inside a GN target.
123SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
124 re.MULTILINE | re.DOTALL)
125
Mirko Bonadei2dcf3482020-06-05 12:30:41126# DEPS_RE matches a block of sources inside a GN target.
127DEPS_RE = re.compile(r'\bdeps \+?= \[(?P<deps>.*?)\]',
128 re.MULTILINE | re.DOTALL)
129
Philipp Hancke0c2a9ca2021-08-11 10:00:27130# FILE_PATH_RE matches a file path.
Mirko Bonadei4dc4e252017-09-19 11:49:16131FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
132
kjellander53047c92015-12-03 07:56:14133
Oleh Prypin2f33a562017-10-04 18:17:54134@contextmanager
135def _AddToPath(*paths):
Jeremy Leconte3d476f22023-10-17 13:04:24136 original_sys_path = sys.path
137 sys.path.extend(paths)
138 try:
139 yield
140 finally:
141 # Restore sys.path to what it was before.
142 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 13:27:22143
144
charujain9893e252017-09-14 11:33:22145def VerifyNativeApiHeadersListIsValid(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24146 """Ensures the list of native API header directories is up to date."""
147 non_existing_paths = []
148 native_api_full_paths = [
149 input_api.os_path.join(input_api.PresubmitLocalPath(),
150 *path.split('/')) for path in API_DIRS
Mirko Bonadei8cc66952020-10-30 09:13:45151 ]
Jeremy Leconte3d476f22023-10-17 13:04:24152 for path in native_api_full_paths:
153 if not os.path.isdir(path):
154 non_existing_paths.append(path)
155 if non_existing_paths:
156 return [
157 output_api.PresubmitError(
158 'Directories to native API headers have changed which has made '
159 'the list in PRESUBMIT.py outdated.\nPlease update it to the '
160 'current location of our native APIs.', non_existing_paths)
161 ]
162 return []
kjellander53047c92015-12-03 07:56:14163
Artem Titove92675b2018-05-22 08:21:27164
kjellanderc88b5d52017-04-05 13:42:43165API_CHANGE_MSG = """
kwibergeb133022016-04-07 14:41:48166You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 12:13:23167 1. Make compatible changes that don't break existing clients. Usually
168 this is done by keeping the existing method signatures unchanged.
Harald Alvestrandf055f9b2024-10-08 20:00:00169 2. Mark the old stuff as deprecated (use the [[deprecated]] attribute or
170 the ABSL_DEPRECATE_AND_INLINE macro).
kwibergeb133022016-04-07 14:41:48171 3. Create a timeline and plan for when the deprecated stuff will be
172 removed. (The amount of time we give users to change their code
173 should be informed by how much work it is for them. If they just
174 need to replace one name with another or something equally
175 simple, 1-2 weeks might be good; if they need to do serious work,
176 up to 3 months may be called for.)
177 4. Update/inform existing downstream code owners to stop using the
178 deprecated stuff. (Send announcements to
179 [email protected] and [email protected].)
180 5. Remove the deprecated stuff, once the agreed-upon amount of time
181 has passed.
182Related files:
183"""
kjellander53047c92015-12-03 07:56:14184
Artem Titove92675b2018-05-22 08:21:27185
charujain9893e252017-09-14 11:33:22186def CheckNativeApiHeaderChanges(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24187 """Checks to remind proper changing of native APIs."""
Dor Hen59d592e2024-09-16 12:48:45188 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24189 source_file_filter = lambda x: input_api.FilterSourceFile(
190 x, files_to_check=[r'.+\.(gn|gni|h)$'])
191 for f in input_api.AffectedSourceFiles(source_file_filter):
192 for path in API_DIRS:
193 dn = os.path.dirname(f.LocalPath())
194 if path == 'api':
195 # Special case: Subdirectories included.
196 if dn == 'api' or dn.startswith('api/'):
Dor Hen59d592e2024-09-16 12:48:45197 files.add(f.LocalPath())
Jeremy Leconte3d476f22023-10-17 13:04:24198 else:
199 # Normal case: Subdirectories not included.
200 if dn == path:
Dor Hen59d592e2024-09-16 12:48:45201 files.add(f.LocalPath())
kjellander53047c92015-12-03 07:56:14202
Jeremy Leconte3d476f22023-10-17 13:04:24203 if files:
Dor Hen59d592e2024-09-16 12:48:45204 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, list(files))]
Jeremy Leconte3d476f22023-10-17 13:04:24205 return []
kjellander53047c92015-12-03 07:56:14206
[email protected]0fcaf992015-11-26 14:24:52207
Mirko Bonadei8cc66952020-10-30 09:13:45208def CheckNoIOStreamInHeaders(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24209 """Checks to make sure no .h files include <iostream>."""
Dor Hen59d592e2024-09-16 12:48:45210 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24211 pattern = input_api.re.compile(r'^#include\s*<iostream>',
212 input_api.re.MULTILINE)
213 file_filter = lambda x: (input_api.FilterSourceFile(x) and
214 source_file_filter(x))
215 for f in input_api.AffectedSourceFiles(file_filter):
216 if not f.LocalPath().endswith('.h'):
217 continue
218 contents = input_api.ReadFile(f)
219 if pattern.search(contents):
Dor Hen59d592e2024-09-16 12:48:45220 files.add(f)
[email protected]51198f12012-02-21 17:53:46221
Jeremy Leconte3d476f22023-10-17 13:04:24222 if len(files) > 0:
223 return [
224 output_api.PresubmitError(
225 'Do not #include <iostream> in header files, since it inserts '
226 'static initialization into every file including the header. '
227 'Instead, #include <ostream>. See http://crbug.com/94794',
Dor Hen59d592e2024-09-16 12:48:45228 list(files))
Jeremy Leconte3d476f22023-10-17 13:04:24229 ]
230 return []
[email protected]51198f12012-02-21 17:53:46231
[email protected]e4158642014-08-06 09:11:18232
Mirko Bonadei8cc66952020-10-30 09:13:45233def CheckNoPragmaOnce(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24234 """Make sure that banned functions are not used."""
Dor Hen59d592e2024-09-16 12:48:45235 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24236 pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE)
237 file_filter = lambda x: (input_api.FilterSourceFile(x) and
238 source_file_filter(x))
239 for f in input_api.AffectedSourceFiles(file_filter):
240 if not f.LocalPath().endswith('.h'):
241 continue
242 contents = input_api.ReadFile(f)
243 if pattern.search(contents):
Dor Hen59d592e2024-09-16 12:48:45244 files.add(f)
kjellander6aeef742017-02-20 09:13:18245
Jeremy Leconte3d476f22023-10-17 13:04:24246 if files:
247 return [
248 output_api.PresubmitError(
249 'Do not use #pragma once in header files.\n'
250 'See http://www.chromium.org/developers/coding-style'
Dor Hen59d592e2024-09-16 12:48:45251 '#TOC-File-headers', list(files))
Jeremy Leconte3d476f22023-10-17 13:04:24252 ]
253 return []
Christoffer Jansson4e8a7732022-02-08 08:01:12254
kjellander6aeef742017-02-20 09:13:18255
Byoungchan Lee94f2ef22021-07-01 13:21:44256def CheckNoFRIEND_TEST(# pylint: disable=invalid-name
Mirko Bonadei8cc66952020-10-30 09:13:45257 input_api,
Byoungchan Lee94f2ef22021-07-01 13:21:44258 output_api,
Mirko Bonadei8cc66952020-10-30 09:13:45259 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24260 """Make sure that gtest's FRIEND_TEST() macro is not used, the
[email protected]51198f12012-02-21 17:53:46261 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
262 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
Jeremy Leconte3d476f22023-10-17 13:04:24263 problems = []
[email protected]51198f12012-02-21 17:53:46264
Jeremy Leconte3d476f22023-10-17 13:04:24265 file_filter = lambda f: (f.LocalPath().endswith(
266 ('.cc', '.h')) and source_file_filter(f))
267 for f in input_api.AffectedFiles(file_filter=file_filter):
268 for line_num, line in f.ChangedContents():
269 if 'FRIEND_TEST(' in line:
270 problems.append(' %s:%d' % (f.LocalPath(), line_num))
[email protected]51198f12012-02-21 17:53:46271
Jeremy Leconte3d476f22023-10-17 13:04:24272 if not problems:
273 return []
274 return [
275 output_api.PresubmitPromptWarning(
276 'WebRTC\'s code should not use gtest\'s FRIEND_TEST() macro. '
277 'Include testsupport/gtest_prod_util.h and use '
278 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))
279 ]
[email protected]51198f12012-02-21 17:53:46280
[email protected]e4158642014-08-06 09:11:18281
Mirko Bonadeifc17a782020-06-30 12:31:37282def IsLintDisabled(disabled_paths, file_path):
Jeremy Leconte3d476f22023-10-17 13:04:24283 """ Checks if a file is disabled for lint check."""
284 for path in disabled_paths:
285 if file_path == path or os.path.dirname(file_path).startswith(path):
286 return True
287 return False
[email protected]0fcaf992015-11-26 14:24:52288
289
charujain9893e252017-09-14 11:33:22290def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 09:23:00291 source_file_filter=None):
Jeremy Leconte3d476f22023-10-17 13:04:24292 """Checks that all new or non-exempt .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 11:33:22293 This check is based on CheckChangeLintsClean in
[email protected]51198f12012-02-21 17:53:46294 depot_tools/presubmit_canned_checks.py but has less filters and only checks
295 added files."""
Jeremy Leconte3d476f22023-10-17 13:04:24296 result = []
[email protected]51198f12012-02-21 17:53:46297
Jeremy Leconte3d476f22023-10-17 13:04:24298 # Initialize cpplint.
299 import cpplint
300 # Access to a protected member _XX of a client class
301 # pylint: disable=W0212
302 cpplint._cpplint_state.ResetErrorCounts()
[email protected]51198f12012-02-21 17:53:46303
Jeremy Leconte3d476f22023-10-17 13:04:24304 lint_filters = cpplint._Filters()
305 lint_filters.extend(DISABLED_LINT_FILTERS)
306 cpplint._SetFilters(','.join(lint_filters))
jbauchc4e3ead2016-02-19 08:25:55307
Jeremy Leconte3d476f22023-10-17 13:04:24308 # Create a platform independent exempt list for cpplint.
309 disabled_paths = [
310 input_api.os_path.join(*path.split('/')) for path in CPPLINT_EXCEPTIONS
311 ]
[email protected]0fcaf992015-11-26 14:24:52312
Jeremy Leconte3d476f22023-10-17 13:04:24313 # Use the strictest verbosity level for cpplint.py (level 1) which is the
314 # default when running cpplint.py from command line. To make it possible to
315 # work with not-yet-converted code, we're only applying it to new (or
316 # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS.
317 verbosity_level = 1
Dor Hen59d592e2024-09-16 12:48:45318 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24319 for f in input_api.AffectedSourceFiles(source_file_filter):
320 # Note that moved/renamed files also count as added.
321 if f.Action() == 'A' or not IsLintDisabled(disabled_paths,
322 f.LocalPath()):
Dor Hen59d592e2024-09-16 12:48:45323 files.add(f.AbsoluteLocalPath())
[email protected]2a452092012-07-01 05:55:23324
Jeremy Leconte3d476f22023-10-17 13:04:24325 for file_name in files:
326 cpplint.ProcessFile(file_name, verbosity_level)
[email protected]51198f12012-02-21 17:53:46327
Jeremy Leconte3d476f22023-10-17 13:04:24328 if cpplint._cpplint_state.error_count > 0:
329 if input_api.is_committing:
330 res_type = output_api.PresubmitError
331 else:
332 res_type = output_api.PresubmitPromptWarning
333 result = [res_type('Changelist failed cpplint.py check.')]
[email protected]51198f12012-02-21 17:53:46334
Jeremy Leconte3d476f22023-10-17 13:04:24335 return result
[email protected]51198f12012-02-21 17:53:46336
Artem Titove92675b2018-05-22 08:21:27337
charujain9893e252017-09-14 11:33:22338def CheckNoSourcesAbove(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24339 # Disallow referencing source files with paths above the GN file location.
340 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
341 re.MULTILINE | re.DOTALL)
342 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
343 violating_gn_files = set()
Dor Hen59d592e2024-09-16 12:48:45344 violating_source_entries = set()
Jeremy Leconte3d476f22023-10-17 13:04:24345 for gn_file in gn_files:
346 contents = input_api.ReadFile(gn_file)
347 for source_block_match in source_pattern.finditer(contents):
348 # Find all source list entries starting with ../ in the source block
349 # (exclude overrides entries).
350 for file_list_match in file_pattern.finditer(
351 source_block_match.group(1)):
352 source_file = file_list_match.group(1)
353 if 'overrides/' not in source_file:
Dor Hen59d592e2024-09-16 12:48:45354 violating_source_entries.add(source_file)
Jeremy Leconte3d476f22023-10-17 13:04:24355 violating_gn_files.add(gn_file)
356 if violating_gn_files:
357 return [
358 output_api.PresubmitError(
359 'Referencing source files above the directory of the GN file '
360 'is not allowed. Please introduce new GN targets in the proper '
361 'location instead.\n'
362 'Invalid source entries:\n'
363 '%s\n'
364 'Violating GN files:' % '\n'.join(violating_source_entries),
Dor Hen59d592e2024-09-16 12:48:45365 items=list(violating_gn_files))
Jeremy Leconte3d476f22023-10-17 13:04:24366 ]
367 return []
ehmaldonado5b1ba082016-09-02 12:51:08368
Artem Titove92675b2018-05-22 08:21:27369
Mirko Bonadei4dc4e252017-09-19 11:49:16370def CheckNoMixingSources(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24371 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
Mirko Bonadei4dc4e252017-09-19 11:49:16372
373 See bugs.webrtc.org/7743 for more context.
374 """
Jeremy Leconte3d476f22023-10-17 13:04:24375 def _MoreThanOneSourceUsed(*sources_lists):
376 sources_used = 0
377 for source_list in sources_lists:
378 if len(source_list) > 0:
379 sources_used += 1
380 return sources_used > 1
Artem Titove92675b2018-05-22 08:21:27381
Jeremy Leconte3d476f22023-10-17 13:04:24382 errors = defaultdict(lambda: [])
383 for gn_file in gn_files:
384 gn_file_content = input_api.ReadFile(gn_file)
385 for target_match in TARGET_RE.finditer(gn_file_content):
386 # list_of_sources is a list of tuples of the form
387 # (c_files, cc_files, objc_files) that keeps track of all the
388 # sources defined in a target. A GN target can have more that
389 # on definition of sources (since it supports if/else statements).
390 # E.g.:
391 # rtc_static_library("foo") {
392 # if (is_win) {
393 # sources = [ "foo.cc" ]
394 # } else {
395 # sources = [ "foo.mm" ]
396 # }
397 # }
398 # This is allowed and the presubmit check should support this case.
399 list_of_sources = []
400 c_files = []
401 cc_files = []
402 objc_files = []
403 target_name = target_match.group('target_name')
404 target_contents = target_match.group('target_contents')
405 for sources_match in SOURCES_RE.finditer(target_contents):
406 if '+=' not in sources_match.group(0):
407 if c_files or cc_files or objc_files:
408 list_of_sources.append((c_files, cc_files, objc_files))
409 c_files = []
410 cc_files = []
411 objc_files = []
412 for file_match in FILE_PATH_RE.finditer(
413 sources_match.group(1)):
414 file_path = file_match.group('file_path')
415 extension = file_match.group('extension')
416 if extension == '.c':
417 c_files.append(file_path + extension)
418 if extension == '.cc':
419 cc_files.append(file_path + extension)
420 if extension in ['.m', '.mm']:
421 objc_files.append(file_path + extension)
Mirko Bonadei4dc4e252017-09-19 11:49:16422 list_of_sources.append((c_files, cc_files, objc_files))
Jeremy Leconte3d476f22023-10-17 13:04:24423 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
424 if _MoreThanOneSourceUsed(c_files_list, cc_files_list,
425 objc_files_list):
426 all_sources = sorted(c_files_list + cc_files_list +
427 objc_files_list)
428 errors[gn_file.LocalPath()].append(
429 (target_name, all_sources))
430 if errors:
431 return [
432 output_api.PresubmitError(
433 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
434 'Please create a separate target for each collection of '
435 'sources.\n'
436 'Mixed sources: \n'
437 '%s\n'
438 'Violating GN files:\n%s\n' %
439 (json.dumps(errors, indent=2), '\n'.join(list(errors.keys()))))
440 ]
441 return []
kjellander7439f972016-12-06 06:47:46442
Artem Titove92675b2018-05-22 08:21:27443
charujain9893e252017-09-14 11:33:22444def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24445 cwd = input_api.PresubmitLocalPath()
446 with _AddToPath(
447 input_api.os_path.join(cwd, 'tools_webrtc',
448 'presubmit_checks_lib')):
449 from check_package_boundaries import CheckPackageBoundaries
450 build_files = [
451 os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files
Mirko Bonadei8cc66952020-10-30 09:13:45452 ]
Jeremy Leconte3d476f22023-10-17 13:04:24453 errors = CheckPackageBoundaries(cwd, build_files)[:5]
454 if errors:
455 return [
456 output_api.PresubmitError(
457 'There are package boundary violations in the following GN '
458 'files:',
459 long_text='\n\n'.join(str(err) for err in errors))
460 ]
461 return []
ehmaldonado4fb97462017-01-30 13:27:22462
Mirko Bonadeia51bbd82018-03-08 15:15:45463
Mirko Bonadeif0e0d752018-07-04 06:48:18464def _ReportFileAndLine(filename, line_num):
Jeremy Leconte3d476f22023-10-17 13:04:24465 """Default error formatter for _FindNewViolationsOfRule."""
466 return '%s (line %s)' % (filename, line_num)
Mirko Bonadeia51bbd82018-03-08 15:15:45467
468
Mirko Bonadei8cc66952020-10-30 09:13:45469def CheckNoWarningSuppressionFlagsAreAdded(gn_files,
470 input_api,
471 output_api,
Mirko Bonadeif0e0d752018-07-04 06:48:18472 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24473 """Ensure warning suppression flags are not added without a reason."""
474 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
475 'in WebRTC.\n'
476 'If you are not adding this code (e.g. you are just moving '
477 'existing code) or you want to add an exception,\n'
478 'you can add a comment on the line that causes the problem:\n\n'
479 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
480 '\n'
481 'Affected files:\n')
482 errors = [] # 2-element tuples with (file, line number)
483 clang_warn_re = input_api.re.compile(
484 r'//build/config/clang:extra_warnings')
485 # pylint: disable-next=fixme
486 no_presubmit_re = input_api.re.compile(
487 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
488 for f in gn_files:
489 for line_num, line in f.ChangedContents():
490 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
491 errors.append(error_formatter(f.LocalPath(), line_num))
492 if errors:
493 return [output_api.PresubmitError(msg, errors)]
494 return []
Mirko Bonadeif0e0d752018-07-04 06:48:18495
Mirko Bonadei9ce800d2019-02-05 15:48:13496
Mirko Bonadei8cc66952020-10-30 09:13:45497def CheckNoTestCaseUsageIsAdded(input_api,
498 output_api,
499 source_file_filter,
Mirko Bonadei9ce800d2019-02-05 15:48:13500 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24501 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
502 'new API: https://github.com/google/googletest/blob/master/'
503 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
504 'Affected files:\n')
505 errors = [] # 2-element tuples with (file, line number)
506 test_case_re = input_api.re.compile(r'TEST_CASE')
507 file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith(
508 '.cc'))
509 for f in input_api.AffectedSourceFiles(file_filter):
510 for line_num, line in f.ChangedContents():
511 if test_case_re.search(line):
512 errors.append(error_formatter(f.LocalPath(), line_num))
513 if errors:
514 return [output_api.PresubmitError(error_msg, errors)]
515 return []
Mirko Bonadei9ce800d2019-02-05 15:48:13516
517
Mirko Bonadei8cc66952020-10-30 09:13:45518def CheckNoStreamUsageIsAdded(input_api,
519 output_api,
Artem Titov739351d2018-05-11 10:21:36520 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 06:48:18521 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24522 """Make sure that no more dependencies on stringstream are added."""
523 error_msg = (
524 'Usage of <sstream>, <istream> and <ostream> in WebRTC is '
525 'deprecated.\n'
526 'This includes the following types:\n'
527 'std::istringstream, std::ostringstream, std::wistringstream, '
528 'std::wostringstream,\n'
529 'std::wstringstream, std::ostream, std::wostream, std::istream,'
530 'std::wistream,\n'
531 'std::iostream, std::wiostream.\n'
532 'If you are not adding this code (e.g. you are just moving '
533 'existing code),\n'
534 'you can add a comment on the line that causes the problem:\n\n'
535 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
536 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
537 '\n'
538 'If you are adding new code, consider using '
Evan Shrubsole4ac5ef02025-04-15 14:49:21539 'webrtc::SimpleStringBuilder\n'
Jeremy Leconte3d476f22023-10-17 13:04:24540 '(in rtc_base/strings/string_builder.h).\n'
541 'Affected files:\n')
542 errors = [] # 2-element tuples with (file, line number)
543 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
544 usage_re = input_api.re.compile(
545 r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
546 no_presubmit_re = input_api.re.compile(
547 r'// no-presubmit-check TODO\(webrtc:8982\)')
548 file_filter = lambda x: (input_api.FilterSourceFile(x) and
549 source_file_filter(x))
Mirko Bonadei571791a2019-05-07 12:08:05550
Jeremy Leconte3d476f22023-10-17 13:04:24551 def _IsException(file_path):
552 is_test = any(
553 file_path.endswith(x) for x in
554 ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc'])
555 return (file_path.startswith('examples')
556 or file_path.startswith('test') or is_test)
Patrik Höglund2ea27962020-01-13 14:10:40557
Jeremy Leconte3d476f22023-10-17 13:04:24558 for f in input_api.AffectedSourceFiles(file_filter):
559 # Usage of stringstream is allowed under examples/ and in tests.
560 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
561 continue
562 for line_num, line in f.ChangedContents():
563 if ((include_re.search(line) or usage_re.search(line))
564 and not no_presubmit_re.search(line)):
565 errors.append(error_formatter(f.LocalPath(), line_num))
566 if errors:
567 return [output_api.PresubmitError(error_msg, errors)]
568 return []
Mirko Bonadeia51bbd82018-03-08 15:15:45569
Artem Titove92675b2018-05-22 08:21:27570
Mirko Bonadeia05d47e2018-05-09 09:03:38571def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24572 """Checks that public_deps is not used without a good reason."""
573 result = []
574 no_presubmit_check_re = input_api.re.compile(
575 r'# no-presubmit-check TODO\(webrtc:\d+\)')
576 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
577 'because it doesn\'t map well to downstream build systems.\n'
578 'Used in: %s (line %d).\n'
579 'If you are not adding this code (e.g. you are just moving '
580 'existing code) or you have a good reason, you can add this '
581 'comment (verbatim) on the line that causes the problem:\n\n'
582 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
583 for affected_file in gn_files:
584 for (line_number, affected_line) in affected_file.ChangedContents():
585 if 'public_deps' in affected_line:
586 surpressed = no_presubmit_check_re.search(affected_line)
587 if not surpressed:
588 result.append(
589 output_api.PresubmitError(
590 error_msg %
591 (affected_file.LocalPath(), line_number)))
592 return result
Mirko Bonadei5c1ad592017-12-12 10:52:27593
Artem Titove92675b2018-05-22 08:21:27594
Mirko Bonadei05691dd2019-10-22 14:34:24595def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24596 result = []
597 error_msg = (
598 'check_includes overrides are not allowed since it can cause '
599 'incorrect dependencies to form. It effectively means that your '
600 'module can include any .h file without depending on its '
601 'corresponding target. There are some exceptional cases when '
602 'this is allowed: if so, get approval from a .gn owner in the '
603 'root OWNERS file.\n'
604 'Used in: %s (line %d).')
605 # pylint: disable-next=fixme
606 no_presubmit_re = input_api.re.compile(
607 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
608 for affected_file in gn_files:
609 for (line_number, affected_line) in affected_file.ChangedContents():
610 if ('check_includes' in affected_line
611 and not no_presubmit_re.search(affected_line)):
612 result.append(
613 output_api.PresubmitError(
614 error_msg % (affected_file.LocalPath(), line_number)))
615 return result
Patrik Höglund6f491062018-01-11 11:04:23616
Artem Titove92675b2018-05-22 08:21:27617
Mirko Bonadeif0e0d752018-07-04 06:48:18618def CheckGnChanges(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24619 file_filter = lambda x: (input_api.FilterSourceFile(
620 x,
621 files_to_check=(r'.+\.(gn|gni)$', ),
622 files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', )))
ehmaldonado5b1ba082016-09-02 12:51:08623
Dor Hen59d592e2024-09-16 12:48:45624 gn_files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24625 for f in input_api.AffectedSourceFiles(file_filter):
Dor Hen59d592e2024-09-16 12:48:45626 gn_files.add(f)
ehmaldonado5b1ba082016-09-02 12:51:08627
Jeremy Leconte3d476f22023-10-17 13:04:24628 result = []
629 if gn_files:
630 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
631 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
Jeremy Leconte3d476f22023-10-17 13:04:24632 result.extend(
633 CheckNoPackageBoundaryViolations(input_api, gn_files, output_api))
634 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api,
635 output_api))
636 result.extend(
637 CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
638 result.extend(
639 CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
640 output_api))
641 return result
ehmaldonado5b1ba082016-09-02 12:51:08642
Artem Titove92675b2018-05-22 08:21:27643
Oleh Prypin920b6532017-10-05 09:28:51644def CheckGnGen(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24645 """Runs `gn gen --check` with default args to detect mismatches between
Oleh Prypin920b6532017-10-05 09:28:51646 #includes and dependencies in the BUILD.gn files, as well as general build
647 errors.
648 """
Jeremy Leconte3d476f22023-10-17 13:04:24649 with _AddToPath(
650 input_api.os_path.join(input_api.PresubmitLocalPath(),
651 'tools_webrtc', 'presubmit_checks_lib')):
Jeremy Leconte53291d42024-08-01 13:45:35652 from build_helpers import run_gn_check
653 errors = run_gn_check(input_api.change.RepositoryRoot())[:5]
Jeremy Leconte3d476f22023-10-17 13:04:24654 if errors:
655 return [
656 output_api.PresubmitPromptWarning(
657 'Some #includes do not match the build dependency graph. '
658 'Please run:\n'
659 ' gn gen --check <out_dir>',
660 long_text='\n\n'.join(errors))
661 ]
662 return []
Oleh Prypin920b6532017-10-05 09:28:51663
Artem Titove92675b2018-05-22 08:21:27664
Artem Titova04d1402018-05-11 09:23:00665def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24666 """Runs checkdeps on #include statements added in this
[email protected]3bd41562014-09-01 11:06:37667 change. Breaking - rules is an error, breaking ! rules is a
668 warning.
669 """
Jeremy Leconte3d476f22023-10-17 13:04:24670 # Copied from Chromium's src/PRESUBMIT.py.
[email protected]3bd41562014-09-01 11:06:37671
Jeremy Leconte3d476f22023-10-17 13:04:24672 # We need to wait until we have an input_api object and use this
673 # roundabout construct to import checkdeps because this file is
674 # eval-ed and thus doesn't have __file__.
Gavin Makfa934322024-07-26 06:33:11675 repo_root = input_api.change.RepositoryRoot()
676 checkdeps_path = input_api.os_path.join(repo_root, 'buildtools',
Jeremy Leconte3d476f22023-10-17 13:04:24677 'checkdeps')
678 if not os.path.exists(checkdeps_path):
679 return [
680 output_api.PresubmitError(
681 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
682 'download all the DEPS entries?' % checkdeps_path)
683 ]
684 with _AddToPath(checkdeps_path):
685 import checkdeps
686 from cpp_checker import CppChecker
687 from rules import Rule
[email protected]3bd41562014-09-01 11:06:37688
Jeremy Leconte3d476f22023-10-17 13:04:24689 added_includes = []
690 for f in input_api.AffectedFiles(file_filter=source_file_filter):
691 if not CppChecker.IsCppFile(f.LocalPath()):
692 continue
[email protected]3bd41562014-09-01 11:06:37693
Jeremy Leconte3d476f22023-10-17 13:04:24694 changed_lines = [line for _, line in f.ChangedContents()]
695 added_includes.append([f.LocalPath(), changed_lines])
[email protected]3bd41562014-09-01 11:06:37696
Jeremy Leconte3d476f22023-10-17 13:04:24697 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
[email protected]3bd41562014-09-01 11:06:37698
Jeremy Leconte3d476f22023-10-17 13:04:24699 error_descriptions = []
700 warning_descriptions = []
701 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
702 added_includes):
703 description_with_path = '%s\n %s' % (path, rule_description)
704 if rule_type == Rule.DISALLOW:
705 error_descriptions.append(description_with_path)
706 else:
707 warning_descriptions.append(description_with_path)
[email protected]3bd41562014-09-01 11:06:37708
Jeremy Leconte3d476f22023-10-17 13:04:24709 results = []
710 if error_descriptions:
711 results.append(
712 output_api.PresubmitError(
713 'You added one or more #includes that violate checkdeps rules.'
714 '\nCheck that the DEPS files in these locations contain valid '
715 'rules.\nSee '
716 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
717 'for more details about checkdeps.', error_descriptions))
718 if warning_descriptions:
719 results.append(
720 output_api.PresubmitPromptOrNotify(
721 'You added one or more #includes of files that are temporarily'
722 '\nallowed but being removed. Can you avoid introducing the\n'
723 '#include? See relevant DEPS file(s) for details and contacts.'
724 '\nSee '
725 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
726 'for more details about checkdeps.', warning_descriptions))
727 return results
[email protected]3bd41562014-09-01 11:06:37728
Artem Titove92675b2018-05-22 08:21:27729
charujain9893e252017-09-14 11:33:22730def CheckCommitMessageBugEntry(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24731 """Check that bug entries are well-formed in commit message."""
732 bogus_bug_msg = (
733 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
734 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.'
735 )
736 results = []
737 for bug in input_api.change.BugsFromDescription():
738 bug = bug.strip()
739 if bug.lower() == 'none':
740 continue
741 if 'b/' not in bug and ':' not in bug:
742 try:
743 if int(bug) > 100000:
744 # Rough indicator for current chromium bugs.
745 prefix_guess = 'chromium'
746 else:
747 prefix_guess = 'webrtc'
748 results.append(
749 'Bug entry requires issue tracker prefix, e.g. %s:%s' %
750 (prefix_guess, bug))
751 except ValueError:
752 results.append(bogus_bug_msg % bug)
753 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
754 results.append(bogus_bug_msg % bug)
755 return [output_api.PresubmitError(r) for r in results]
charujain9893e252017-09-14 11:33:22756
Artem Titove92675b2018-05-22 08:21:27757
charujain9893e252017-09-14 11:33:22758def CheckChangeHasBugField(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24759 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 15:11:16760
761 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 13:12:35762 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 15:11:16763 a bug reference.
Mirko Bonadei61880182017-10-12 13:12:35764
765 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
766 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 15:11:16767 """
Jeremy Leconte3d476f22023-10-17 13:04:24768 if input_api.change.BugsFromDescription():
769 return []
770 return [
771 output_api.PresubmitError(
772 'The "Bug: [bug number]" footer is mandatory. Please create a '
773 'bug and reference it using either of:\n'
774 ' * https://bugs.webrtc.org - reference it using Bug: '
775 'webrtc:XXXX\n'
776 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')
777 ]
[email protected]e4158642014-08-06 09:11:18778
Artem Titove92675b2018-05-22 08:21:27779
Artem Titova04d1402018-05-11 09:23:00780def CheckJSONParseErrors(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24781 """Check that JSON files do not contain syntax errors."""
782 def FilterFile(affected_file):
783 return (input_api.os_path.splitext(affected_file.LocalPath())[1]
784 == '.json' and source_file_filter(affected_file))
kjellander569cf942016-02-11 13:02:59785
Jeremy Leconte3d476f22023-10-17 13:04:24786 def GetJSONParseError(input_api, filename):
787 try:
788 contents = input_api.ReadFile(filename)
789 input_api.json.loads(contents)
790 except ValueError as e:
791 return e
792 return None
kjellander569cf942016-02-11 13:02:59793
Jeremy Leconte3d476f22023-10-17 13:04:24794 results = []
795 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
796 include_deletes=False):
797 parse_error = GetJSONParseError(input_api,
798 affected_file.AbsoluteLocalPath())
799 if parse_error:
800 results.append(
801 output_api.PresubmitError(
802 '%s could not be parsed: %s' %
803 (affected_file.LocalPath(), parse_error)))
804 return results
kjellander569cf942016-02-11 13:02:59805
806
charujain9893e252017-09-14 11:33:22807def RunPythonTests(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24808 def Join(*args):
809 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
Henrik Kjellander8d3ad822015-05-26 17:52:05810
Jeremy Leconte3d476f22023-10-17 13:04:24811 excluded_files = [
812 # These tests should be run manually after webrtc_dashboard_upload
813 # target has been built.
814 'catapult_uploader_test.py',
815 'process_perf_results_test.py',
816 ]
Christoffer Jansson70098a82022-02-21 18:43:36817
Jeremy Leconte3d476f22023-10-17 13:04:24818 test_directories = [
819 input_api.PresubmitLocalPath(),
820 Join('rtc_tools', 'py_event_log_analyzer'),
821 ] + [
822 root for root, _, files in os.walk(Join('tools_webrtc')) if any(
823 f.endswith('_test.py') and f not in excluded_files for f in files)
824 ]
Henrik Kjellander8d3ad822015-05-26 17:52:05825
Jeremy Leconte3d476f22023-10-17 13:04:24826 tests = []
Christoffer Jansson1b083a92022-02-15 13:52:31827
Jeremy Leconte3d476f22023-10-17 13:04:24828 for directory in test_directories:
829 tests.extend(
830 input_api.canned_checks.GetUnitTestsInDirectory(
831 input_api,
832 output_api,
833 directory,
834 files_to_check=[r'.+_test\.py$'],
835 run_on_python2=False))
836 return input_api.RunTests(tests, parallel=True)
Henrik Kjellander8d3ad822015-05-26 17:52:05837
838
Artem Titova04d1402018-05-11 09:23:00839def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
840 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24841 """Checks that the namespace google::protobuf has not been used."""
Dor Hen59d592e2024-09-16 12:48:45842 files = set()
Jeremy Leconte3d476f22023-10-17 13:04:24843 pattern = input_api.re.compile(r'google::protobuf')
844 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
845 file_filter = lambda x: (input_api.FilterSourceFile(x) and
846 source_file_filter(x))
847 for f in input_api.AffectedSourceFiles(file_filter):
848 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
849 continue
850 contents = input_api.ReadFile(f)
851 if pattern.search(contents):
Dor Hen59d592e2024-09-16 12:48:45852 files.add(f)
mbonadei38415b22017-04-07 12:38:01853
Jeremy Leconte3d476f22023-10-17 13:04:24854 if files:
855 return [
856 output_api.PresubmitError(
857 'Please avoid to use namespace `google::protobuf` directly.\n'
858 'Add a using directive in `%s` and include that header instead.'
Dor Hen59d592e2024-09-16 12:48:45859 % proto_utils_path, list(files))
Jeremy Leconte3d476f22023-10-17 13:04:24860 ]
861 return []
mbonadei38415b22017-04-07 12:38:01862
863
Mirko Bonadei92ea95e2017-09-15 04:47:31864def _LicenseHeader(input_api):
Jeremy Leconte3d476f22023-10-17 13:04:24865 """Returns the license header regexp."""
866 # Accept any year number from 2003 to the current year
867 current_year = int(input_api.time.strftime('%Y'))
868 allowed_years = (str(s) for s in reversed(range(2003, current_year + 1)))
869 years_re = '(' + '|'.join(allowed_years) + ')'
870 license_header = (
871 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
872 r'All [Rr]ights [Rr]eserved\.\n'
873 r'.*?\n'
874 r'.*? Use of this source code is governed by a BSD-style license\n'
875 r'.*? that can be found in the LICENSE file in the root of the source\n'
876 r'.*? tree\. An additional intellectual property rights grant can be '
877 r'found\n'
878 r'.*? in the file PATENTS\. All contributing project authors may\n'
879 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
880 ) % {
881 'year': years_re,
882 }
883 return license_header
Mirko Bonadei92ea95e2017-09-15 04:47:31884
885
charujain9893e252017-09-14 11:33:22886def CommonChecks(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24887 """Checks common to both upload and commit."""
888 results = []
889 # Filter out files that are in objc or ios dirs from being cpplint-ed since
890 # they do not follow C++ lint rules.
891 exception_list = input_api.DEFAULT_FILES_TO_SKIP + (
892 r".*\bobjc[\\\/].*",
893 r".*objc\.[hcm]+$",
894 )
895 source_file_filter = lambda x: input_api.FilterSourceFile(
896 x, None, exception_list)
897 results.extend(
898 CheckApprovedFilesLintClean(input_api, output_api, source_file_filter))
899 results.extend(
900 input_api.canned_checks.CheckLicense(input_api, output_api,
901 _LicenseHeader(input_api)))
Byoungchan Lee94f2ef22021-07-01 13:21:44902
Jeremy Leconte3d476f22023-10-17 13:04:24903 # TODO(bugs.webrtc.org/12114): Delete this filter and run pylint on
904 # all python files. This is a temporary solution.
905 python_file_filter = lambda f: (f.LocalPath().endswith('.py') and
906 source_file_filter(f))
907 python_changed_files = [
908 f.LocalPath()
909 for f in input_api.AffectedFiles(include_deletes=False,
910 file_filter=python_file_filter)
911 ]
912 pylint_new_style = [
913 f for f in python_changed_files if f not in PYLINT_OLD_STYLE
914 ]
915 pylint_old_style = [
916 f for f in python_changed_files if f in PYLINT_OLD_STYLE
917 ]
918 if pylint_new_style:
919 results.extend(
920 input_api.canned_checks.RunPylint(
921 input_api,
922 output_api,
923 files_to_check=pylint_new_style,
924 files_to_skip=(
925 r'^base[\\\/].*\.py$',
926 r'^build[\\\/].*\.py$',
927 r'^buildtools[\\\/].*\.py$',
928 r'^infra[\\\/].*\.py$',
929 r'^ios[\\\/].*\.py$',
930 r'^out.*[\\\/].*\.py$',
931 r'^testing[\\\/].*\.py$',
932 r'^third_party[\\\/].*\.py$',
933 r'^tools[\\\/].*\.py$',
934 r'^xcodebuild.*[\\\/].*\.py$',
935 ),
936 pylintrc='pylintrc',
937 version='2.7'))
Byoungchan Lee94f2ef22021-07-01 13:21:44938
Jeremy Leconte3d476f22023-10-17 13:04:24939 if pylint_old_style:
940 results.extend(
941 input_api.canned_checks.RunPylint(input_api,
942 output_api,
943 files_to_check=pylint_old_style,
944 pylintrc='pylintrc_old_style',
945 version='2.7'))
946 # TODO(bugs.webrtc.org/13606): talk/ is no more, so make below checks
947 # simpler. WebRTC can't use the presubmit_canned_checks.PanProjectChecks
948 # function since we need to have different license checks in talk/ and
949 # webrtc/directories. Instead, hand-picked checks are included below.
kjellander569cf942016-02-11 13:02:59950
Jeremy Leconte3d476f22023-10-17 13:04:24951 # .m and .mm files are ObjC files. For simplicity we will consider
952 # .h files in ObjC subdirectories ObjC headers.
953 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
954 # Skip long-lines check for DEPS and GN files.
955 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
956 # Also we will skip most checks for third_party directory.
957 third_party_filter_list = (r'(^|.*[\\\/])third_party[\\\/].+', )
958 eighty_char_sources = lambda x: input_api.FilterSourceFile(
959 x,
960 files_to_skip=build_file_filter_list + objc_filter_list +
961 third_party_filter_list)
962 hundred_char_sources = lambda x: input_api.FilterSourceFile(
963 x, files_to_check=objc_filter_list)
964 non_third_party_sources = lambda x: input_api.FilterSourceFile(
965 x, files_to_skip=third_party_filter_list)
Henrik Kjellander63224672015-09-08 06:03:56966
Jeremy Leconte3d476f22023-10-17 13:04:24967 results.extend(
968 input_api.canned_checks.CheckLongLines(
969 input_api,
970 output_api,
971 maxlen=80,
972 source_file_filter=eighty_char_sources))
973 results.extend(
974 input_api.canned_checks.CheckLongLines(
975 input_api,
976 output_api,
977 maxlen=100,
978 source_file_filter=hundred_char_sources))
979 results.extend(
980 input_api.canned_checks.CheckChangeHasNoTabs(
981 input_api, output_api, source_file_filter=non_third_party_sources))
982 results.extend(
983 input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
984 input_api, output_api, source_file_filter=non_third_party_sources))
985 results.extend(
986 input_api.canned_checks.CheckAuthorizedAuthor(
987 input_api,
988 output_api,
989 bot_allowlist=[
990 '[email protected]',
991 '[email protected]',
Gennady Tsitovich8ccd71d2025-07-15 08:24:50992 ('chrome-cherry-picker'
993 '@chops-service-accounts.iam.gserviceaccount.com'),
Jeremy Leconte3d476f22023-10-17 13:04:24994 ]))
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
[email protected]2442de12012-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
[email protected]e4158642014-08-06 09:11:181272
[email protected]53df1362012-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
[email protected]da159d62011-05-30 11:51:341280
[email protected]e4158642014-08-06 09:11:181281
[email protected]2442de12012-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 []