blob: bd3bbc3913e661eb1a2f49881bfd92c5d93d5c00 [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.
Danil Chapovalov7013b3b2021-02-22 13:31:26171 2. Mark the old stuff as deprecated (use the ABSL_DEPRECATED macro).
kwibergeb133022016-04-07 14:41:48172 3. Create a timeline and plan for when the deprecated stuff will be
173 removed. (The amount of time we give users to change their code
174 should be informed by how much work it is for them. If they just
175 need to replace one name with another or something equally
176 simple, 1-2 weeks might be good; if they need to do serious work,
177 up to 3 months may be called for.)
178 4. Update/inform existing downstream code owners to stop using the
179 deprecated stuff. (Send announcements to
180 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
181 5. Remove the deprecated stuff, once the agreed-upon amount of time
182 has passed.
183Related files:
184"""
kjellander53047c92015-12-03 07:56:14185
Artem Titove92675b2018-05-22 08:21:27186
charujain9893e252017-09-14 11:33:22187def CheckNativeApiHeaderChanges(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24188 """Checks to remind proper changing of native APIs."""
189 files = []
190 source_file_filter = lambda x: input_api.FilterSourceFile(
191 x, files_to_check=[r'.+\.(gn|gni|h)$'])
192 for f in input_api.AffectedSourceFiles(source_file_filter):
193 for path in API_DIRS:
194 dn = os.path.dirname(f.LocalPath())
195 if path == 'api':
196 # Special case: Subdirectories included.
197 if dn == 'api' or dn.startswith('api/'):
198 files.append(f.LocalPath())
199 else:
200 # Normal case: Subdirectories not included.
201 if dn == path:
202 files.append(f.LocalPath())
kjellander53047c92015-12-03 07:56:14203
Jeremy Leconte3d476f22023-10-17 13:04:24204 if files:
205 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
206 return []
kjellander53047c92015-12-03 07:56:14207
kjellander@webrtc.org0fcaf992015-11-26 14:24:52208
Mirko Bonadei8cc66952020-10-30 09:13:45209def CheckNoIOStreamInHeaders(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24210 """Checks to make sure no .h files include <iostream>."""
211 files = []
212 pattern = input_api.re.compile(r'^#include\s*<iostream>',
213 input_api.re.MULTILINE)
214 file_filter = lambda x: (input_api.FilterSourceFile(x) and
215 source_file_filter(x))
216 for f in input_api.AffectedSourceFiles(file_filter):
217 if not f.LocalPath().endswith('.h'):
218 continue
219 contents = input_api.ReadFile(f)
220 if pattern.search(contents):
221 files.append(f)
kjellander@webrtc.org51198f12012-02-21 17:53:46222
Jeremy Leconte3d476f22023-10-17 13:04:24223 if len(files) > 0:
224 return [
225 output_api.PresubmitError(
226 'Do not #include <iostream> in header files, since it inserts '
227 'static initialization into every file including the header. '
228 'Instead, #include <ostream>. See http://crbug.com/94794',
229 files)
230 ]
231 return []
kjellander@webrtc.org51198f12012-02-21 17:53:46232
kjellander@webrtc.orge4158642014-08-06 09:11:18233
Mirko Bonadei8cc66952020-10-30 09:13:45234def CheckNoPragmaOnce(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24235 """Make sure that banned functions are not used."""
236 files = []
237 pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE)
238 file_filter = lambda x: (input_api.FilterSourceFile(x) and
239 source_file_filter(x))
240 for f in input_api.AffectedSourceFiles(file_filter):
241 if not f.LocalPath().endswith('.h'):
242 continue
243 contents = input_api.ReadFile(f)
244 if pattern.search(contents):
245 files.append(f)
kjellander6aeef742017-02-20 09:13:18246
Jeremy Leconte3d476f22023-10-17 13:04:24247 if files:
248 return [
249 output_api.PresubmitError(
250 'Do not use #pragma once in header files.\n'
251 'See http://www.chromium.org/developers/coding-style'
252 '#TOC-File-headers', files)
253 ]
254 return []
Christoffer Jansson4e8a7732022-02-08 08:01:12255
kjellander6aeef742017-02-20 09:13:18256
Byoungchan Lee94f2ef22021-07-01 13:21:44257def CheckNoFRIEND_TEST(# pylint: disable=invalid-name
Mirko Bonadei8cc66952020-10-30 09:13:45258 input_api,
Byoungchan Lee94f2ef22021-07-01 13:21:44259 output_api,
Mirko Bonadei8cc66952020-10-30 09:13:45260 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24261 """Make sure that gtest's FRIEND_TEST() macro is not used, the
kjellander@webrtc.org51198f12012-02-21 17:53:46262 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
263 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
Jeremy Leconte3d476f22023-10-17 13:04:24264 problems = []
kjellander@webrtc.org51198f12012-02-21 17:53:46265
Jeremy Leconte3d476f22023-10-17 13:04:24266 file_filter = lambda f: (f.LocalPath().endswith(
267 ('.cc', '.h')) and source_file_filter(f))
268 for f in input_api.AffectedFiles(file_filter=file_filter):
269 for line_num, line in f.ChangedContents():
270 if 'FRIEND_TEST(' in line:
271 problems.append(' %s:%d' % (f.LocalPath(), line_num))
kjellander@webrtc.org51198f12012-02-21 17:53:46272
Jeremy Leconte3d476f22023-10-17 13:04:24273 if not problems:
274 return []
275 return [
276 output_api.PresubmitPromptWarning(
277 'WebRTC\'s code should not use gtest\'s FRIEND_TEST() macro. '
278 'Include testsupport/gtest_prod_util.h and use '
279 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))
280 ]
kjellander@webrtc.org51198f12012-02-21 17:53:46281
kjellander@webrtc.orge4158642014-08-06 09:11:18282
Mirko Bonadeifc17a782020-06-30 12:31:37283def IsLintDisabled(disabled_paths, file_path):
Jeremy Leconte3d476f22023-10-17 13:04:24284 """ Checks if a file is disabled for lint check."""
285 for path in disabled_paths:
286 if file_path == path or os.path.dirname(file_path).startswith(path):
287 return True
288 return False
kjellander@webrtc.org0fcaf992015-11-26 14:24:52289
290
charujain9893e252017-09-14 11:33:22291def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 09:23:00292 source_file_filter=None):
Jeremy Leconte3d476f22023-10-17 13:04:24293 """Checks that all new or non-exempt .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 11:33:22294 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46295 depot_tools/presubmit_canned_checks.py but has less filters and only checks
296 added files."""
Jeremy Leconte3d476f22023-10-17 13:04:24297 result = []
kjellander@webrtc.org51198f12012-02-21 17:53:46298
Jeremy Leconte3d476f22023-10-17 13:04:24299 # Initialize cpplint.
300 import cpplint
301 # Access to a protected member _XX of a client class
302 # pylint: disable=W0212
303 cpplint._cpplint_state.ResetErrorCounts()
kjellander@webrtc.org51198f12012-02-21 17:53:46304
Jeremy Leconte3d476f22023-10-17 13:04:24305 lint_filters = cpplint._Filters()
306 lint_filters.extend(DISABLED_LINT_FILTERS)
307 cpplint._SetFilters(','.join(lint_filters))
jbauchc4e3ead2016-02-19 08:25:55308
Jeremy Leconte3d476f22023-10-17 13:04:24309 # Create a platform independent exempt list for cpplint.
310 disabled_paths = [
311 input_api.os_path.join(*path.split('/')) for path in CPPLINT_EXCEPTIONS
312 ]
kjellander@webrtc.org0fcaf992015-11-26 14:24:52313
Jeremy Leconte3d476f22023-10-17 13:04:24314 # Use the strictest verbosity level for cpplint.py (level 1) which is the
315 # default when running cpplint.py from command line. To make it possible to
316 # work with not-yet-converted code, we're only applying it to new (or
317 # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS.
318 verbosity_level = 1
319 files = []
320 for f in input_api.AffectedSourceFiles(source_file_filter):
321 # Note that moved/renamed files also count as added.
322 if f.Action() == 'A' or not IsLintDisabled(disabled_paths,
323 f.LocalPath()):
324 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23325
Jeremy Leconte3d476f22023-10-17 13:04:24326 for file_name in files:
327 cpplint.ProcessFile(file_name, verbosity_level)
kjellander@webrtc.org51198f12012-02-21 17:53:46328
Jeremy Leconte3d476f22023-10-17 13:04:24329 if cpplint._cpplint_state.error_count > 0:
330 if input_api.is_committing:
331 res_type = output_api.PresubmitError
332 else:
333 res_type = output_api.PresubmitPromptWarning
334 result = [res_type('Changelist failed cpplint.py check.')]
kjellander@webrtc.org51198f12012-02-21 17:53:46335
Jeremy Leconte3d476f22023-10-17 13:04:24336 return result
kjellander@webrtc.org51198f12012-02-21 17:53:46337
Artem Titove92675b2018-05-22 08:21:27338
charujain9893e252017-09-14 11:33:22339def CheckNoSourcesAbove(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24340 # Disallow referencing source files with paths above the GN file location.
341 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
342 re.MULTILINE | re.DOTALL)
343 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
344 violating_gn_files = set()
345 violating_source_entries = []
346 for gn_file in gn_files:
347 contents = input_api.ReadFile(gn_file)
348 for source_block_match in source_pattern.finditer(contents):
349 # Find all source list entries starting with ../ in the source block
350 # (exclude overrides entries).
351 for file_list_match in file_pattern.finditer(
352 source_block_match.group(1)):
353 source_file = file_list_match.group(1)
354 if 'overrides/' not in source_file:
355 violating_source_entries.append(source_file)
356 violating_gn_files.add(gn_file)
357 if violating_gn_files:
358 return [
359 output_api.PresubmitError(
360 'Referencing source files above the directory of the GN file '
361 'is not allowed. Please introduce new GN targets in the proper '
362 'location instead.\n'
363 'Invalid source entries:\n'
364 '%s\n'
365 'Violating GN files:' % '\n'.join(violating_source_entries),
366 items=violating_gn_files)
367 ]
368 return []
ehmaldonado5b1ba082016-09-02 12:51:08369
Artem Titove92675b2018-05-22 08:21:27370
Mirko Bonadei4dc4e252017-09-19 11:49:16371def CheckNoMixingSources(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24372 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
Mirko Bonadei4dc4e252017-09-19 11:49:16373
374 See bugs.webrtc.org/7743 for more context.
375 """
Jeremy Leconte3d476f22023-10-17 13:04:24376 def _MoreThanOneSourceUsed(*sources_lists):
377 sources_used = 0
378 for source_list in sources_lists:
379 if len(source_list) > 0:
380 sources_used += 1
381 return sources_used > 1
Artem Titove92675b2018-05-22 08:21:27382
Jeremy Leconte3d476f22023-10-17 13:04:24383 errors = defaultdict(lambda: [])
384 for gn_file in gn_files:
385 gn_file_content = input_api.ReadFile(gn_file)
386 for target_match in TARGET_RE.finditer(gn_file_content):
387 # list_of_sources is a list of tuples of the form
388 # (c_files, cc_files, objc_files) that keeps track of all the
389 # sources defined in a target. A GN target can have more that
390 # on definition of sources (since it supports if/else statements).
391 # E.g.:
392 # rtc_static_library("foo") {
393 # if (is_win) {
394 # sources = [ "foo.cc" ]
395 # } else {
396 # sources = [ "foo.mm" ]
397 # }
398 # }
399 # This is allowed and the presubmit check should support this case.
400 list_of_sources = []
401 c_files = []
402 cc_files = []
403 objc_files = []
404 target_name = target_match.group('target_name')
405 target_contents = target_match.group('target_contents')
406 for sources_match in SOURCES_RE.finditer(target_contents):
407 if '+=' not in sources_match.group(0):
408 if c_files or cc_files or objc_files:
409 list_of_sources.append((c_files, cc_files, objc_files))
410 c_files = []
411 cc_files = []
412 objc_files = []
413 for file_match in FILE_PATH_RE.finditer(
414 sources_match.group(1)):
415 file_path = file_match.group('file_path')
416 extension = file_match.group('extension')
417 if extension == '.c':
418 c_files.append(file_path + extension)
419 if extension == '.cc':
420 cc_files.append(file_path + extension)
421 if extension in ['.m', '.mm']:
422 objc_files.append(file_path + extension)
Mirko Bonadei4dc4e252017-09-19 11:49:16423 list_of_sources.append((c_files, cc_files, objc_files))
Jeremy Leconte3d476f22023-10-17 13:04:24424 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
425 if _MoreThanOneSourceUsed(c_files_list, cc_files_list,
426 objc_files_list):
427 all_sources = sorted(c_files_list + cc_files_list +
428 objc_files_list)
429 errors[gn_file.LocalPath()].append(
430 (target_name, all_sources))
431 if errors:
432 return [
433 output_api.PresubmitError(
434 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
435 'Please create a separate target for each collection of '
436 'sources.\n'
437 'Mixed sources: \n'
438 '%s\n'
439 'Violating GN files:\n%s\n' %
440 (json.dumps(errors, indent=2), '\n'.join(list(errors.keys()))))
441 ]
442 return []
kjellander7439f972016-12-06 06:47:46443
Artem Titove92675b2018-05-22 08:21:27444
charujain9893e252017-09-14 11:33:22445def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24446 cwd = input_api.PresubmitLocalPath()
447 with _AddToPath(
448 input_api.os_path.join(cwd, 'tools_webrtc',
449 'presubmit_checks_lib')):
450 from check_package_boundaries import CheckPackageBoundaries
451 build_files = [
452 os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files
Mirko Bonadei8cc66952020-10-30 09:13:45453 ]
Jeremy Leconte3d476f22023-10-17 13:04:24454 errors = CheckPackageBoundaries(cwd, build_files)[:5]
455 if errors:
456 return [
457 output_api.PresubmitError(
458 'There are package boundary violations in the following GN '
459 'files:',
460 long_text='\n\n'.join(str(err) for err in errors))
461 ]
462 return []
ehmaldonado4fb97462017-01-30 13:27:22463
Mirko Bonadeia51bbd82018-03-08 15:15:45464
Mirko Bonadeif0e0d752018-07-04 06:48:18465def _ReportFileAndLine(filename, line_num):
Jeremy Leconte3d476f22023-10-17 13:04:24466 """Default error formatter for _FindNewViolationsOfRule."""
467 return '%s (line %s)' % (filename, line_num)
Mirko Bonadeia51bbd82018-03-08 15:15:45468
469
Mirko Bonadei8cc66952020-10-30 09:13:45470def CheckNoWarningSuppressionFlagsAreAdded(gn_files,
471 input_api,
472 output_api,
Mirko Bonadeif0e0d752018-07-04 06:48:18473 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24474 """Ensure warning suppression flags are not added without a reason."""
475 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
476 'in WebRTC.\n'
477 'If you are not adding this code (e.g. you are just moving '
478 'existing code) or you want to add an exception,\n'
479 'you can add a comment on the line that causes the problem:\n\n'
480 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
481 '\n'
482 'Affected files:\n')
483 errors = [] # 2-element tuples with (file, line number)
484 clang_warn_re = input_api.re.compile(
485 r'//build/config/clang:extra_warnings')
486 # pylint: disable-next=fixme
487 no_presubmit_re = input_api.re.compile(
488 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
489 for f in gn_files:
490 for line_num, line in f.ChangedContents():
491 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
492 errors.append(error_formatter(f.LocalPath(), line_num))
493 if errors:
494 return [output_api.PresubmitError(msg, errors)]
495 return []
Mirko Bonadeif0e0d752018-07-04 06:48:18496
Mirko Bonadei9ce800d2019-02-05 15:48:13497
Mirko Bonadei8cc66952020-10-30 09:13:45498def CheckNoTestCaseUsageIsAdded(input_api,
499 output_api,
500 source_file_filter,
Mirko Bonadei9ce800d2019-02-05 15:48:13501 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24502 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
503 'new API: https://github.com/google/googletest/blob/master/'
504 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
505 'Affected files:\n')
506 errors = [] # 2-element tuples with (file, line number)
507 test_case_re = input_api.re.compile(r'TEST_CASE')
508 file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith(
509 '.cc'))
510 for f in input_api.AffectedSourceFiles(file_filter):
511 for line_num, line in f.ChangedContents():
512 if test_case_re.search(line):
513 errors.append(error_formatter(f.LocalPath(), line_num))
514 if errors:
515 return [output_api.PresubmitError(error_msg, errors)]
516 return []
Mirko Bonadei9ce800d2019-02-05 15:48:13517
518
Mirko Bonadei8cc66952020-10-30 09:13:45519def CheckNoStreamUsageIsAdded(input_api,
520 output_api,
Artem Titov739351d2018-05-11 10:21:36521 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 06:48:18522 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24523 """Make sure that no more dependencies on stringstream are added."""
524 error_msg = (
525 'Usage of <sstream>, <istream> and <ostream> in WebRTC is '
526 'deprecated.\n'
527 'This includes the following types:\n'
528 'std::istringstream, std::ostringstream, std::wistringstream, '
529 'std::wostringstream,\n'
530 'std::wstringstream, std::ostream, std::wostream, std::istream,'
531 'std::wistream,\n'
532 'std::iostream, std::wiostream.\n'
533 'If you are not adding this code (e.g. you are just moving '
534 'existing code),\n'
535 'you can add a comment on the line that causes the problem:\n\n'
536 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
537 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
538 '\n'
539 'If you are adding new code, consider using '
540 'rtc::SimpleStringBuilder\n'
541 '(in rtc_base/strings/string_builder.h).\n'
542 'Affected files:\n')
543 errors = [] # 2-element tuples with (file, line number)
544 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
545 usage_re = input_api.re.compile(
546 r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
547 no_presubmit_re = input_api.re.compile(
548 r'// no-presubmit-check TODO\(webrtc:8982\)')
549 file_filter = lambda x: (input_api.FilterSourceFile(x) and
550 source_file_filter(x))
Mirko Bonadei571791a2019-05-07 12:08:05551
Jeremy Leconte3d476f22023-10-17 13:04:24552 def _IsException(file_path):
553 is_test = any(
554 file_path.endswith(x) for x in
555 ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc'])
556 return (file_path.startswith('examples')
557 or file_path.startswith('test') or is_test)
Patrik Höglund2ea27962020-01-13 14:10:40558
Jeremy Leconte3d476f22023-10-17 13:04:24559 for f in input_api.AffectedSourceFiles(file_filter):
560 # Usage of stringstream is allowed under examples/ and in tests.
561 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
562 continue
563 for line_num, line in f.ChangedContents():
564 if ((include_re.search(line) or usage_re.search(line))
565 and not no_presubmit_re.search(line)):
566 errors.append(error_formatter(f.LocalPath(), line_num))
567 if errors:
568 return [output_api.PresubmitError(error_msg, errors)]
569 return []
Mirko Bonadeia51bbd82018-03-08 15:15:45570
Artem Titove92675b2018-05-22 08:21:27571
Mirko Bonadeia05d47e2018-05-09 09:03:38572def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24573 """Checks that public_deps is not used without a good reason."""
574 result = []
575 no_presubmit_check_re = input_api.re.compile(
576 r'# no-presubmit-check TODO\(webrtc:\d+\)')
577 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
578 'because it doesn\'t map well to downstream build systems.\n'
579 'Used in: %s (line %d).\n'
580 'If you are not adding this code (e.g. you are just moving '
581 'existing code) or you have a good reason, you can add this '
582 'comment (verbatim) on the line that causes the problem:\n\n'
583 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
584 for affected_file in gn_files:
585 for (line_number, affected_line) in affected_file.ChangedContents():
586 if 'public_deps' in affected_line:
587 surpressed = no_presubmit_check_re.search(affected_line)
588 if not surpressed:
589 result.append(
590 output_api.PresubmitError(
591 error_msg %
592 (affected_file.LocalPath(), line_number)))
593 return result
Mirko Bonadei5c1ad592017-12-12 10:52:27594
Artem Titove92675b2018-05-22 08:21:27595
Mirko Bonadei05691dd2019-10-22 14:34:24596def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24597 result = []
598 error_msg = (
599 'check_includes overrides are not allowed since it can cause '
600 'incorrect dependencies to form. It effectively means that your '
601 'module can include any .h file without depending on its '
602 'corresponding target. There are some exceptional cases when '
603 'this is allowed: if so, get approval from a .gn owner in the '
604 'root OWNERS file.\n'
605 'Used in: %s (line %d).')
606 # pylint: disable-next=fixme
607 no_presubmit_re = input_api.re.compile(
608 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
609 for affected_file in gn_files:
610 for (line_number, affected_line) in affected_file.ChangedContents():
611 if ('check_includes' in affected_line
612 and not no_presubmit_re.search(affected_line)):
613 result.append(
614 output_api.PresubmitError(
615 error_msg % (affected_file.LocalPath(), line_number)))
616 return result
Patrik Höglund6f491062018-01-11 11:04:23617
Artem Titove92675b2018-05-22 08:21:27618
Mirko Bonadeif0e0d752018-07-04 06:48:18619def CheckGnChanges(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24620 file_filter = lambda x: (input_api.FilterSourceFile(
621 x,
622 files_to_check=(r'.+\.(gn|gni)$', ),
623 files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', )))
ehmaldonado5b1ba082016-09-02 12:51:08624
Jeremy Leconte3d476f22023-10-17 13:04:24625 gn_files = []
626 for f in input_api.AffectedSourceFiles(file_filter):
627 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 12:51:08628
Jeremy Leconte3d476f22023-10-17 13:04:24629 result = []
630 if gn_files:
631 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
632 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
Jeremy Leconte3d476f22023-10-17 13:04:24633 result.extend(
634 CheckNoPackageBoundaryViolations(input_api, gn_files, output_api))
635 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api,
636 output_api))
637 result.extend(
638 CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
639 result.extend(
640 CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
641 output_api))
642 return result
ehmaldonado5b1ba082016-09-02 12:51:08643
Artem Titove92675b2018-05-22 08:21:27644
Oleh Prypin920b6532017-10-05 09:28:51645def CheckGnGen(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24646 """Runs `gn gen --check` with default args to detect mismatches between
Oleh Prypin920b6532017-10-05 09:28:51647 #includes and dependencies in the BUILD.gn files, as well as general build
648 errors.
649 """
Jeremy Leconte3d476f22023-10-17 13:04:24650 with _AddToPath(
651 input_api.os_path.join(input_api.PresubmitLocalPath(),
652 'tools_webrtc', 'presubmit_checks_lib')):
Jeremy Leconte53291d42024-08-01 13:45:35653 from build_helpers import run_gn_check
654 errors = run_gn_check(input_api.change.RepositoryRoot())[:5]
Jeremy Leconte3d476f22023-10-17 13:04:24655 if errors:
656 return [
657 output_api.PresubmitPromptWarning(
658 'Some #includes do not match the build dependency graph. '
659 'Please run:\n'
660 ' gn gen --check <out_dir>',
661 long_text='\n\n'.join(errors))
662 ]
663 return []
Oleh Prypin920b6532017-10-05 09:28:51664
Artem Titove92675b2018-05-22 08:21:27665
Artem Titova04d1402018-05-11 09:23:00666def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24667 """Runs checkdeps on #include statements added in this
kjellander@webrtc.org3bd41562014-09-01 11:06:37668 change. Breaking - rules is an error, breaking ! rules is a
669 warning.
670 """
Jeremy Leconte3d476f22023-10-17 13:04:24671 # Copied from Chromium's src/PRESUBMIT.py.
kjellander@webrtc.org3bd41562014-09-01 11:06:37672
Jeremy Leconte3d476f22023-10-17 13:04:24673 # We need to wait until we have an input_api object and use this
674 # roundabout construct to import checkdeps because this file is
675 # eval-ed and thus doesn't have __file__.
Gavin Makfa934322024-07-26 06:33:11676 repo_root = input_api.change.RepositoryRoot()
677 checkdeps_path = input_api.os_path.join(repo_root, 'buildtools',
Jeremy Leconte3d476f22023-10-17 13:04:24678 'checkdeps')
679 if not os.path.exists(checkdeps_path):
680 return [
681 output_api.PresubmitError(
682 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
683 'download all the DEPS entries?' % checkdeps_path)
684 ]
685 with _AddToPath(checkdeps_path):
686 import checkdeps
687 from cpp_checker import CppChecker
688 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37689
Jeremy Leconte3d476f22023-10-17 13:04:24690 added_includes = []
691 for f in input_api.AffectedFiles(file_filter=source_file_filter):
692 if not CppChecker.IsCppFile(f.LocalPath()):
693 continue
kjellander@webrtc.org3bd41562014-09-01 11:06:37694
Jeremy Leconte3d476f22023-10-17 13:04:24695 changed_lines = [line for _, line in f.ChangedContents()]
696 added_includes.append([f.LocalPath(), changed_lines])
kjellander@webrtc.org3bd41562014-09-01 11:06:37697
Jeremy Leconte3d476f22023-10-17 13:04:24698 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
kjellander@webrtc.org3bd41562014-09-01 11:06:37699
Jeremy Leconte3d476f22023-10-17 13:04:24700 error_descriptions = []
701 warning_descriptions = []
702 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
703 added_includes):
704 description_with_path = '%s\n %s' % (path, rule_description)
705 if rule_type == Rule.DISALLOW:
706 error_descriptions.append(description_with_path)
707 else:
708 warning_descriptions.append(description_with_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37709
Jeremy Leconte3d476f22023-10-17 13:04:24710 results = []
711 if error_descriptions:
712 results.append(
713 output_api.PresubmitError(
714 'You added one or more #includes that violate checkdeps rules.'
715 '\nCheck that the DEPS files in these locations contain valid '
716 'rules.\nSee '
717 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
718 'for more details about checkdeps.', error_descriptions))
719 if warning_descriptions:
720 results.append(
721 output_api.PresubmitPromptOrNotify(
722 'You added one or more #includes of files that are temporarily'
723 '\nallowed but being removed. Can you avoid introducing the\n'
724 '#include? See relevant DEPS file(s) for details and contacts.'
725 '\nSee '
726 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
727 'for more details about checkdeps.', warning_descriptions))
728 return results
kjellander@webrtc.org3bd41562014-09-01 11:06:37729
Artem Titove92675b2018-05-22 08:21:27730
charujain9893e252017-09-14 11:33:22731def CheckCommitMessageBugEntry(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24732 """Check that bug entries are well-formed in commit message."""
733 bogus_bug_msg = (
734 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
735 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.'
736 )
737 results = []
738 for bug in input_api.change.BugsFromDescription():
739 bug = bug.strip()
740 if bug.lower() == 'none':
741 continue
742 if 'b/' not in bug and ':' not in bug:
743 try:
744 if int(bug) > 100000:
745 # Rough indicator for current chromium bugs.
746 prefix_guess = 'chromium'
747 else:
748 prefix_guess = 'webrtc'
749 results.append(
750 'Bug entry requires issue tracker prefix, e.g. %s:%s' %
751 (prefix_guess, bug))
752 except ValueError:
753 results.append(bogus_bug_msg % bug)
754 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
755 results.append(bogus_bug_msg % bug)
756 return [output_api.PresubmitError(r) for r in results]
charujain9893e252017-09-14 11:33:22757
Artem Titove92675b2018-05-22 08:21:27758
charujain9893e252017-09-14 11:33:22759def CheckChangeHasBugField(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24760 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 15:11:16761
762 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 13:12:35763 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 15:11:16764 a bug reference.
Mirko Bonadei61880182017-10-12 13:12:35765
766 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
767 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 15:11:16768 """
Jeremy Leconte3d476f22023-10-17 13:04:24769 if input_api.change.BugsFromDescription():
770 return []
771 return [
772 output_api.PresubmitError(
773 'The "Bug: [bug number]" footer is mandatory. Please create a '
774 'bug and reference it using either of:\n'
775 ' * https://bugs.webrtc.org - reference it using Bug: '
776 'webrtc:XXXX\n'
777 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')
778 ]
kjellander@webrtc.orge4158642014-08-06 09:11:18779
Artem Titove92675b2018-05-22 08:21:27780
Artem Titova04d1402018-05-11 09:23:00781def CheckJSONParseErrors(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24782 """Check that JSON files do not contain syntax errors."""
783 def FilterFile(affected_file):
784 return (input_api.os_path.splitext(affected_file.LocalPath())[1]
785 == '.json' and source_file_filter(affected_file))
kjellander569cf942016-02-11 13:02:59786
Jeremy Leconte3d476f22023-10-17 13:04:24787 def GetJSONParseError(input_api, filename):
788 try:
789 contents = input_api.ReadFile(filename)
790 input_api.json.loads(contents)
791 except ValueError as e:
792 return e
793 return None
kjellander569cf942016-02-11 13:02:59794
Jeremy Leconte3d476f22023-10-17 13:04:24795 results = []
796 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
797 include_deletes=False):
798 parse_error = GetJSONParseError(input_api,
799 affected_file.AbsoluteLocalPath())
800 if parse_error:
801 results.append(
802 output_api.PresubmitError(
803 '%s could not be parsed: %s' %
804 (affected_file.LocalPath(), parse_error)))
805 return results
kjellander569cf942016-02-11 13:02:59806
807
charujain9893e252017-09-14 11:33:22808def RunPythonTests(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24809 def Join(*args):
810 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
Henrik Kjellander8d3ad822015-05-26 17:52:05811
Jeremy Leconte3d476f22023-10-17 13:04:24812 excluded_files = [
813 # These tests should be run manually after webrtc_dashboard_upload
814 # target has been built.
815 'catapult_uploader_test.py',
816 'process_perf_results_test.py',
817 ]
Christoffer Jansson70098a82022-02-21 18:43:36818
Jeremy Leconte3d476f22023-10-17 13:04:24819 test_directories = [
820 input_api.PresubmitLocalPath(),
821 Join('rtc_tools', 'py_event_log_analyzer'),
822 ] + [
823 root for root, _, files in os.walk(Join('tools_webrtc')) if any(
824 f.endswith('_test.py') and f not in excluded_files for f in files)
825 ]
Henrik Kjellander8d3ad822015-05-26 17:52:05826
Jeremy Leconte3d476f22023-10-17 13:04:24827 tests = []
Christoffer Jansson1b083a92022-02-15 13:52:31828
Jeremy Leconte3d476f22023-10-17 13:04:24829 for directory in test_directories:
830 tests.extend(
831 input_api.canned_checks.GetUnitTestsInDirectory(
832 input_api,
833 output_api,
834 directory,
835 files_to_check=[r'.+_test\.py$'],
836 run_on_python2=False))
837 return input_api.RunTests(tests, parallel=True)
Henrik Kjellander8d3ad822015-05-26 17:52:05838
839
Artem Titova04d1402018-05-11 09:23:00840def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
841 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24842 """Checks that the namespace google::protobuf has not been used."""
843 files = []
844 pattern = input_api.re.compile(r'google::protobuf')
845 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
846 file_filter = lambda x: (input_api.FilterSourceFile(x) and
847 source_file_filter(x))
848 for f in input_api.AffectedSourceFiles(file_filter):
849 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
850 continue
851 contents = input_api.ReadFile(f)
852 if pattern.search(contents):
853 files.append(f)
mbonadei38415b22017-04-07 12:38:01854
Jeremy Leconte3d476f22023-10-17 13:04:24855 if files:
856 return [
857 output_api.PresubmitError(
858 'Please avoid to use namespace `google::protobuf` directly.\n'
859 'Add a using directive in `%s` and include that header instead.'
860 % proto_utils_path, files)
861 ]
862 return []
mbonadei38415b22017-04-07 12:38:01863
864
Mirko Bonadei92ea95e2017-09-15 04:47:31865def _LicenseHeader(input_api):
Jeremy Leconte3d476f22023-10-17 13:04:24866 """Returns the license header regexp."""
867 # Accept any year number from 2003 to the current year
868 current_year = int(input_api.time.strftime('%Y'))
869 allowed_years = (str(s) for s in reversed(range(2003, current_year + 1)))
870 years_re = '(' + '|'.join(allowed_years) + ')'
871 license_header = (
872 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
873 r'All [Rr]ights [Rr]eserved\.\n'
874 r'.*?\n'
875 r'.*? Use of this source code is governed by a BSD-style license\n'
876 r'.*? that can be found in the LICENSE file in the root of the source\n'
877 r'.*? tree\. An additional intellectual property rights grant can be '
878 r'found\n'
879 r'.*? in the file PATENTS\. All contributing project authors may\n'
880 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
881 ) % {
882 'year': years_re,
883 }
884 return license_header
Mirko Bonadei92ea95e2017-09-15 04:47:31885
886
charujain9893e252017-09-14 11:33:22887def CommonChecks(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24888 """Checks common to both upload and commit."""
889 results = []
890 # Filter out files that are in objc or ios dirs from being cpplint-ed since
891 # they do not follow C++ lint rules.
892 exception_list = input_api.DEFAULT_FILES_TO_SKIP + (
893 r".*\bobjc[\\\/].*",
894 r".*objc\.[hcm]+$",
895 )
896 source_file_filter = lambda x: input_api.FilterSourceFile(
897 x, None, exception_list)
898 results.extend(
899 CheckApprovedFilesLintClean(input_api, output_api, source_file_filter))
900 results.extend(
901 input_api.canned_checks.CheckLicense(input_api, output_api,
902 _LicenseHeader(input_api)))
Byoungchan Lee94f2ef22021-07-01 13:21:44903
Jeremy Leconte3d476f22023-10-17 13:04:24904 # TODO(bugs.webrtc.org/12114): Delete this filter and run pylint on
905 # all python files. This is a temporary solution.
906 python_file_filter = lambda f: (f.LocalPath().endswith('.py') and
907 source_file_filter(f))
908 python_changed_files = [
909 f.LocalPath()
910 for f in input_api.AffectedFiles(include_deletes=False,
911 file_filter=python_file_filter)
912 ]
913 pylint_new_style = [
914 f for f in python_changed_files if f not in PYLINT_OLD_STYLE
915 ]
916 pylint_old_style = [
917 f for f in python_changed_files if f in PYLINT_OLD_STYLE
918 ]
919 if pylint_new_style:
920 results.extend(
921 input_api.canned_checks.RunPylint(
922 input_api,
923 output_api,
924 files_to_check=pylint_new_style,
925 files_to_skip=(
926 r'^base[\\\/].*\.py$',
927 r'^build[\\\/].*\.py$',
928 r'^buildtools[\\\/].*\.py$',
929 r'^infra[\\\/].*\.py$',
930 r'^ios[\\\/].*\.py$',
931 r'^out.*[\\\/].*\.py$',
932 r'^testing[\\\/].*\.py$',
933 r'^third_party[\\\/].*\.py$',
934 r'^tools[\\\/].*\.py$',
935 r'^xcodebuild.*[\\\/].*\.py$',
936 ),
937 pylintrc='pylintrc',
938 version='2.7'))
Byoungchan Lee94f2ef22021-07-01 13:21:44939
Jeremy Leconte3d476f22023-10-17 13:04:24940 if pylint_old_style:
941 results.extend(
942 input_api.canned_checks.RunPylint(input_api,
943 output_api,
944 files_to_check=pylint_old_style,
945 pylintrc='pylintrc_old_style',
946 version='2.7'))
947 # TODO(bugs.webrtc.org/13606): talk/ is no more, so make below checks
948 # simpler. WebRTC can't use the presubmit_canned_checks.PanProjectChecks
949 # function since we need to have different license checks in talk/ and
950 # webrtc/directories. Instead, hand-picked checks are included below.
kjellander569cf942016-02-11 13:02:59951
Jeremy Leconte3d476f22023-10-17 13:04:24952 # .m and .mm files are ObjC files. For simplicity we will consider
953 # .h files in ObjC subdirectories ObjC headers.
954 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
955 # Skip long-lines check for DEPS and GN files.
956 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
957 # Also we will skip most checks for third_party directory.
958 third_party_filter_list = (r'(^|.*[\\\/])third_party[\\\/].+', )
959 eighty_char_sources = lambda x: input_api.FilterSourceFile(
960 x,
961 files_to_skip=build_file_filter_list + objc_filter_list +
962 third_party_filter_list)
963 hundred_char_sources = lambda x: input_api.FilterSourceFile(
964 x, files_to_check=objc_filter_list)
965 non_third_party_sources = lambda x: input_api.FilterSourceFile(
966 x, files_to_skip=third_party_filter_list)
Henrik Kjellander63224672015-09-08 06:03:56967
Jeremy Leconte3d476f22023-10-17 13:04:24968 results.extend(
969 input_api.canned_checks.CheckLongLines(
970 input_api,
971 output_api,
972 maxlen=80,
973 source_file_filter=eighty_char_sources))
974 results.extend(
975 input_api.canned_checks.CheckLongLines(
976 input_api,
977 output_api,
978 maxlen=100,
979 source_file_filter=hundred_char_sources))
980 results.extend(
981 input_api.canned_checks.CheckChangeHasNoTabs(
982 input_api, output_api, source_file_filter=non_third_party_sources))
983 results.extend(
984 input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
985 input_api, output_api, source_file_filter=non_third_party_sources))
986 results.extend(
987 input_api.canned_checks.CheckAuthorizedAuthor(
988 input_api,
989 output_api,
990 bot_allowlist=[
991 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com',
992 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com',
993 ]))
994 results.extend(
995 input_api.canned_checks.CheckChangeTodoHasOwner(
996 input_api, output_api, source_file_filter=non_third_party_sources))
997 results.extend(
998 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
999 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
1000 results.extend(
1001 CheckNoIOStreamInHeaders(input_api,
1002 output_api,
1003 source_file_filter=non_third_party_sources))
1004 results.extend(
1005 CheckNoPragmaOnce(input_api,
1006 output_api,
1007 source_file_filter=non_third_party_sources))
1008 results.extend(
1009 CheckNoFRIEND_TEST(input_api,
Mirko Bonadei8cc66952020-10-30 09:13:451010 output_api,
1011 source_file_filter=non_third_party_sources))
Jeremy Leconte3d476f22023-10-17 13:04:241012 results.extend(CheckGnChanges(input_api, output_api))
1013 results.extend(
1014 CheckUnwantedDependencies(input_api,
1015 output_api,
1016 source_file_filter=non_third_party_sources))
1017 results.extend(
1018 CheckJSONParseErrors(input_api,
1019 output_api,
1020 source_file_filter=non_third_party_sources))
1021 results.extend(RunPythonTests(input_api, output_api))
1022 results.extend(
1023 CheckUsageOfGoogleProtobufNamespace(
1024 input_api, output_api, source_file_filter=non_third_party_sources))
1025 results.extend(
1026 CheckOrphanHeaders(input_api,
1027 output_api,
1028 source_file_filter=non_third_party_sources))
1029 results.extend(
1030 CheckNewlineAtTheEndOfProtoFiles(
1031 input_api, output_api, source_file_filter=non_third_party_sources))
1032 results.extend(
Byoungchan Leeeb76f192024-01-22 16:55:141033 CheckLFNewline(input_api, output_api, non_third_party_sources))
1034 results.extend(
Jeremy Leconte3d476f22023-10-17 13:04:241035 CheckNoStreamUsageIsAdded(input_api, output_api,
Mirko Bonadei8cc66952020-10-30 09:13:451036 non_third_party_sources))
Jeremy Leconte3d476f22023-10-17 13:04:241037 results.extend(
1038 CheckNoTestCaseUsageIsAdded(input_api, output_api,
1039 non_third_party_sources))
1040 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1041 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
1042 results.extend(
1043 CheckAbslMemoryInclude(input_api, output_api, non_third_party_sources))
1044 results.extend(
1045 CheckAssertUsage(input_api, output_api, non_third_party_sources))
1046 results.extend(
1047 CheckBannedAbslMakeUnique(input_api, output_api,
1048 non_third_party_sources))
1049 results.extend(
1050 CheckObjcApiSymbols(input_api, output_api, non_third_party_sources))
1051 return results
Mirko Bonadeia418e672018-10-24 11:57:251052
1053
1054def CheckApiDepsFileIsUpToDate(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241055 """Check that 'include_rules' in api/DEPS is up to date.
Mirko Bonadei90490372018-10-26 11:17:471056
1057 The file api/DEPS must be kept up to date in order to avoid to avoid to
1058 include internal header from WebRTC's api/ headers.
1059
1060 This check is focused on ensuring that 'include_rules' contains a deny
1061 rule for each root level directory. More focused allow rules can be
1062 added to 'specific_include_rules'.
1063 """
Jeremy Leconte3d476f22023-10-17 13:04:241064 results = []
1065 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
1066 with open(api_deps) as f:
1067 deps_content = _ParseDeps(f.read())
Mirko Bonadeia418e672018-10-24 11:57:251068
Jeremy Leconte3d476f22023-10-17 13:04:241069 include_rules = deps_content.get('include_rules', [])
1070 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 11:57:251071
Jeremy Leconte3d476f22023-10-17 13:04:241072 # Only check top level directories affected by the current CL.
1073 dirs_to_check = set()
1074 for f in input_api.AffectedFiles():
1075 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
1076 if len(path_tokens) > 1:
1077 if (path_tokens[0] not in dirs_to_skip and os.path.isdir(
1078 os.path.join(input_api.PresubmitLocalPath(),
1079 path_tokens[0]))):
1080 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 11:57:251081
Jeremy Leconte3d476f22023-10-17 13:04:241082 missing_include_rules = set()
1083 for p in dirs_to_check:
1084 rule = '-%s' % p
1085 if rule not in include_rules:
1086 missing_include_rules.add(rule)
Mirko Bonadei90490372018-10-26 11:17:471087
Jeremy Leconte3d476f22023-10-17 13:04:241088 if missing_include_rules:
1089 error_msg = [
1090 'include_rules = [\n',
1091 ' ...\n',
1092 ]
Mirko Bonadeia418e672018-10-24 11:57:251093
Jeremy Leconte3d476f22023-10-17 13:04:241094 for r in sorted(missing_include_rules):
1095 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 11:57:251096
Jeremy Leconte3d476f22023-10-17 13:04:241097 error_msg.append(' ...\n')
1098 error_msg.append(']\n')
Mirko Bonadei90490372018-10-26 11:17:471099
Jeremy Leconte3d476f22023-10-17 13:04:241100 results.append(
1101 output_api.PresubmitError(
1102 'New root level directory detected! WebRTC api/ headers should '
1103 'not #include headers from \n'
1104 'the new directory, so please update "include_rules" in file\n'
1105 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
Mirko Bonadei90490372018-10-26 11:17:471106
Jeremy Leconte3d476f22023-10-17 13:04:241107 return results
Mirko Bonadei8cc66952020-10-30 09:13:451108
andrew@webrtc.org2442de12012-01-23 17:45:411109
Mirko Bonadei9fa8ef12019-09-17 17:14:131110def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241111 file_filter = lambda f: (f.LocalPath().endswith(
1112 ('.cc', '.h')) and source_file_filter(f))
Mirko Bonadei9fa8ef12019-09-17 17:14:131113
Jeremy Leconte3d476f22023-10-17 13:04:241114 files = []
1115 for f in input_api.AffectedFiles(include_deletes=False,
1116 file_filter=file_filter):
1117 for _, line in f.ChangedContents():
1118 if 'absl::make_unique' in line:
1119 files.append(f)
1120 break
Mirko Bonadei9fa8ef12019-09-17 17:14:131121
Jeremy Leconte3d476f22023-10-17 13:04:241122 if files:
1123 return [
1124 output_api.PresubmitError(
1125 'Please use std::make_unique instead of absl::make_unique.\n'
1126 'Affected files:', files)
1127 ]
1128 return []
Mirko Bonadei8cc66952020-10-30 09:13:451129
Mirko Bonadei9fa8ef12019-09-17 17:14:131130
Mirko Bonadeid74c0e62020-07-16 19:57:011131def CheckObjcApiSymbols(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241132 rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}',
1133 re.MULTILINE | re.DOTALL)
1134 file_filter = lambda f: (f.LocalPath().endswith(
1135 ('.h')) and source_file_filter(f))
Mirko Bonadeid74c0e62020-07-16 19:57:011136
Jeremy Leconte3d476f22023-10-17 13:04:241137 files = []
1138 file_filter = lambda x: (input_api.FilterSourceFile(x) and
1139 source_file_filter(x))
1140 for f in input_api.AffectedSourceFiles(file_filter):
1141 if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath():
1142 continue
1143 if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'):
1144 continue
1145 contents = input_api.ReadFile(f)
1146 for match in rtc_objc_export.finditer(contents):
1147 export_block = match.group(0)
1148 if 'RTC_OBJC_TYPE' not in export_block:
1149 files.append(f.LocalPath())
Mirko Bonadeid74c0e62020-07-16 19:57:011150
Jeremy Leconte3d476f22023-10-17 13:04:241151 if len(files) > 0:
1152 return [
1153 output_api.PresubmitError(
1154 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() '
1155 + 'macro.\n\n' + 'For example:\n' +
1156 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' +
1157 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' +
1158 'Please fix the following files:', files)
1159 ]
1160 return []
Mirko Bonadei8cc66952020-10-30 09:13:451161
Mirko Bonadeid74c0e62020-07-16 19:57:011162
Mirko Bonadeia6395132021-07-22 15:35:591163def CheckAssertUsage(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241164 pattern = input_api.re.compile(r'\bassert\(')
1165 file_filter = lambda f: (f.LocalPath().endswith(
1166 ('.cc', '.h', '.m', '.mm')) and source_file_filter(f))
Mirko Bonadeia6395132021-07-22 15:35:591167
Jeremy Leconte3d476f22023-10-17 13:04:241168 files = []
1169 for f in input_api.AffectedFiles(include_deletes=False,
1170 file_filter=file_filter):
1171 for _, line in f.ChangedContents():
1172 if pattern.search(line):
1173 files.append(f.LocalPath())
1174 break
Mirko Bonadeia6395132021-07-22 15:35:591175
Jeremy Leconte3d476f22023-10-17 13:04:241176 if len(files) > 0:
1177 return [
1178 output_api.PresubmitError(
1179 'Usage of assert() has been detected in the following files, '
1180 'please use RTC_DCHECK() instead.\n Files:', files)
1181 ]
1182 return []
Mirko Bonadeia6395132021-07-22 15:35:591183
1184
tzika06bf852018-11-15 11:37:351185def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241186 pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"',
1187 input_api.re.MULTILINE)
1188 file_filter = lambda f: (f.LocalPath().endswith(
1189 ('.cc', '.h')) and source_file_filter(f))
tzika06bf852018-11-15 11:37:351190
Jeremy Leconte3d476f22023-10-17 13:04:241191 files = []
1192 for f in input_api.AffectedFiles(include_deletes=False,
1193 file_filter=file_filter):
1194 contents = input_api.ReadFile(f)
1195 if pattern.search(contents):
1196 continue
1197 for _, line in f.ChangedContents():
1198 if 'absl::WrapUnique' in line:
1199 files.append(f)
1200 break
tzika06bf852018-11-15 11:37:351201
Jeremy Leconte3d476f22023-10-17 13:04:241202 if len(files) > 0:
1203 return [
1204 output_api.PresubmitError(
1205 'Please include "absl/memory/memory.h" header for '
1206 'absl::WrapUnique.\nThis header may or may not be included '
1207 'transitively depending on the C++ standard version.', files)
1208 ]
1209 return []
Mirko Bonadei8cc66952020-10-30 09:13:451210
kjellander@webrtc.orge4158642014-08-06 09:11:181211
andrew@webrtc.org53df1362012-01-26 21:24:231212def CheckChangeOnUpload(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241213 results = []
1214 results.extend(CommonChecks(input_api, output_api))
1215 results.extend(CheckGnGen(input_api, output_api))
1216 results.extend(
1217 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
1218 return results
niklase@google.comda159d62011-05-30 11:51:341219
kjellander@webrtc.orge4158642014-08-06 09:11:181220
andrew@webrtc.org2442de12012-01-23 17:45:411221def CheckChangeOnCommit(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241222 results = []
1223 results.extend(CommonChecks(input_api, output_api))
1224 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
1225 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
1226 results.extend(
1227 input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api))
1228 results.extend(
1229 input_api.canned_checks.CheckChangeHasDescription(
1230 input_api, output_api))
1231 results.extend(CheckChangeHasBugField(input_api, output_api))
1232 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
1233 results.extend(
1234 input_api.canned_checks.CheckTreeIsOpen(
1235 input_api,
1236 output_api,
1237 json_url='http://webrtc-status.appspot.com/current?format=json'))
1238 return results
mbonadei74973ed2017-05-09 14:58:051239
1240
Artem Titova04d1402018-05-11 09:23:001241def CheckOrphanHeaders(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241242 # We need to wait until we have an input_api object and use this
1243 # roundabout construct to import prebubmit_checks_lib because this file is
1244 # eval-ed and thus doesn't have __file__.
1245 error_msg = """{} should be listed in {}."""
1246 results = []
1247 exempt_paths = [re.escape(os.path.join('tools_webrtc', 'ios', 'SDK'))]
Christoffer Jansson884e8ae2022-02-11 20:29:381248
Jeremy Leconte3d476f22023-10-17 13:04:241249 with _AddToPath(
1250 input_api.os_path.join(input_api.PresubmitLocalPath(),
1251 'tools_webrtc', 'presubmit_checks_lib')):
1252 from check_orphan_headers import GetBuildGnPathFromFilePath
1253 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 14:58:051254
Jeremy Leconte3d476f22023-10-17 13:04:241255 file_filter = lambda x: input_api.FilterSourceFile(
1256 x, files_to_skip=exempt_paths) and source_file_filter(x)
1257 for f in input_api.AffectedSourceFiles(file_filter):
1258 if f.LocalPath().endswith('.h'):
1259 file_path = os.path.abspath(f.LocalPath())
1260 root_dir = os.getcwd()
1261 gn_file_path = GetBuildGnPathFromFilePath(file_path,
1262 os.path.exists, root_dir)
1263 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1264 if not in_build_gn:
1265 results.append(
1266 output_api.PresubmitError(
1267 error_msg.format(f.LocalPath(),
1268 os.path.relpath(gn_file_path))))
1269 return results
Mirko Bonadei960fd5b2017-06-29 12:59:361270
1271
Mirko Bonadei8cc66952020-10-30 09:13:451272def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api,
1273 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241274 """Checks that all .proto files are terminated with a newline."""
1275 error_msg = 'File {} must end with exactly one newline.'
1276 results = []
1277 file_filter = lambda x: input_api.FilterSourceFile(
1278 x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x)
1279 for f in input_api.AffectedSourceFiles(file_filter):
1280 file_path = f.LocalPath()
1281 with open(file_path) as f:
1282 lines = f.readlines()
1283 if len(lines) > 0 and not lines[-1].endswith('\n'):
1284 results.append(
1285 output_api.PresubmitError(error_msg.format(file_path)))
1286 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231287
1288
Byoungchan Leeeb76f192024-01-22 16:55:141289def CheckLFNewline(input_api, output_api, source_file_filter):
1290 """Checks that all files have LF newlines."""
1291 error_msg = 'File {} must use LF newlines.'
1292 results = []
1293 file_filter = lambda x: input_api.FilterSourceFile(
1294 x, files_to_check=(r'.+', )) and source_file_filter(x)
1295 for f in input_api.AffectedSourceFiles(file_filter):
1296 file_path = f.LocalPath()
1297 with open(file_path, 'rb') as f:
1298 if b'\r\n' in f.read():
1299 results.append(
1300 output_api.PresubmitError(error_msg.format(file_path)))
1301 return results
1302
Mirko Bonadei7e4ee6e2018-09-28 09:45:231303def _ExtractAddRulesFromParsedDeps(parsed_deps):
Jeremy Leconte3d476f22023-10-17 13:04:241304 """Extract the rules that add dependencies from a parsed DEPS file.
Mirko Bonadei7e4ee6e2018-09-28 09:45:231305
1306 Args:
1307 parsed_deps: the locals dictionary from evaluating the DEPS file."""
Jeremy Leconte3d476f22023-10-17 13:04:241308 add_rules = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231309 add_rules.update([
Jeremy Leconte3d476f22023-10-17 13:04:241310 rule[1:] for rule in parsed_deps.get('include_rules', [])
Mirko Bonadei7e4ee6e2018-09-28 09:45:231311 if rule.startswith('+') or rule.startswith('!')
1312 ])
Jeremy Leconte3d476f22023-10-17 13:04:241313 for _, rules in parsed_deps.get('specific_include_rules', {}).items():
1314 add_rules.update([
1315 rule[1:] for rule in rules
1316 if rule.startswith('+') or rule.startswith('!')
1317 ])
1318 return add_rules
Mirko Bonadei7e4ee6e2018-09-28 09:45:231319
1320
1321def _ParseDeps(contents):
Jeremy Leconte3d476f22023-10-17 13:04:241322 """Simple helper for parsing DEPS files."""
Mirko Bonadei7e4ee6e2018-09-28 09:45:231323
Jeremy Leconte3d476f22023-10-17 13:04:241324 # Stubs for handling special syntax in the root DEPS file.
1325 class VarImpl:
1326 def __init__(self, local_scope):
1327 self._local_scope = local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231328
Jeremy Leconte3d476f22023-10-17 13:04:241329 def Lookup(self, var_name):
1330 """Implements the Var syntax."""
1331 try:
1332 return self._local_scope['vars'][var_name]
1333 except KeyError as var_not_defined:
1334 raise Exception('Var is not defined: %s' %
1335 var_name) from var_not_defined
Mirko Bonadei7e4ee6e2018-09-28 09:45:231336
Jeremy Leconte3d476f22023-10-17 13:04:241337 local_scope = {}
1338 global_scope = {
1339 'Var': VarImpl(local_scope).Lookup,
1340 }
1341 exec(contents, global_scope, local_scope)
1342 return local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231343
1344
1345def _CalculateAddedDeps(os_path, old_contents, new_contents):
Jeremy Leconte3d476f22023-10-17 13:04:241346 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
Mirko Bonadei7e4ee6e2018-09-28 09:45:231347 a set of DEPS entries that we should look up.
1348
1349 For a directory (rather than a specific filename) we fake a path to
1350 a specific filename by adding /DEPS. This is chosen as a file that
1351 will seldom or never be subject to per-file include_rules.
1352 """
Jeremy Leconte3d476f22023-10-17 13:04:241353 # We ignore deps entries on auto-generated directories.
1354 auto_generated_dirs = ['grit', 'jni']
Mirko Bonadei7e4ee6e2018-09-28 09:45:231355
Jeremy Leconte3d476f22023-10-17 13:04:241356 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1357 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231358
Jeremy Leconte3d476f22023-10-17 13:04:241359 added_deps = new_deps.difference(old_deps)
Mirko Bonadei7e4ee6e2018-09-28 09:45:231360
Jeremy Leconte3d476f22023-10-17 13:04:241361 results = set()
1362 for added_dep in added_deps:
1363 if added_dep.split('/')[0] in auto_generated_dirs:
1364 continue
1365 # Assume that a rule that ends in .h is a rule for a specific file.
1366 if added_dep.endswith('.h'):
1367 results.add(added_dep)
1368 else:
1369 results.add(os_path.join(added_dep, 'DEPS'))
1370 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231371
1372
1373def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241374 """When a dependency prefixed with + is added to a DEPS file, we
Edward Lesmesbef08502021-02-05 22:08:321375 want to make sure that the change is reviewed by an OWNER of the
1376 target file or directory, to avoid layering violations from being
1377 introduced. This check verifies that this happens.
1378 """
Jeremy Leconte3d476f22023-10-17 13:04:241379 virtual_depended_on_files = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231380
Jeremy Leconte3d476f22023-10-17 13:04:241381 file_filter = lambda f: not input_api.re.match(
1382 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1383 for f in input_api.AffectedFiles(include_deletes=False,
1384 file_filter=file_filter):
1385 filename = input_api.os_path.basename(f.LocalPath())
1386 if filename == 'DEPS':
1387 virtual_depended_on_files.update(
1388 _CalculateAddedDeps(input_api.os_path,
1389 '\n'.join(f.OldContents()),
1390 '\n'.join(f.NewContents())))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231391
Jeremy Leconte3d476f22023-10-17 13:04:241392 if not virtual_depended_on_files:
1393 return []
Christoffer Jansson4e8a7732022-02-08 08:01:121394
Jeremy Leconte3d476f22023-10-17 13:04:241395 if input_api.is_committing:
1396 if input_api.tbr:
1397 return [
1398 output_api.PresubmitNotifyResult(
1399 '--tbr was specified, skipping OWNERS check for DEPS '
1400 'additions')
1401 ]
1402 if input_api.dry_run:
1403 return [
1404 output_api.PresubmitNotifyResult(
1405 'This is a dry run, skipping OWNERS check for DEPS '
1406 'additions')
1407 ]
1408 if not input_api.change.issue:
1409 return [
1410 output_api.PresubmitError(
1411 "DEPS approval by OWNERS check failed: this change has "
1412 "no change number, so we can't check it for approvals.")
1413 ]
1414 output = output_api.PresubmitError
1415 else:
1416 output = output_api.PresubmitNotifyResult
Christoffer Jansson4e8a7732022-02-08 08:01:121417
Jeremy Leconte3d476f22023-10-17 13:04:241418 owner_email, reviewers = (
1419 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1420 input_api, None, approval_needed=input_api.is_committing))
Christoffer Jansson4e8a7732022-02-08 08:01:121421
Jeremy Leconte3d476f22023-10-17 13:04:241422 owner_email = owner_email or input_api.change.author_email
Christoffer Jansson4e8a7732022-02-08 08:01:121423
Jeremy Leconte3d476f22023-10-17 13:04:241424 approval_status = input_api.owners_client.GetFilesApprovalStatus(
1425 virtual_depended_on_files, reviewers.union([owner_email]), [])
1426 missing_files = [
1427 f for f in virtual_depended_on_files
1428 if approval_status[f] != input_api.owners_client.APPROVED
Christoffer Jansson4e8a7732022-02-08 08:01:121429 ]
Christoffer Jansson4e8a7732022-02-08 08:01:121430
Jeremy Leconte3d476f22023-10-17 13:04:241431 # We strip the /DEPS part that was added by
1432 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1433 # directory.
1434 def StripDeps(path):
1435 start_deps = path.rfind('/DEPS')
1436 if start_deps != -1:
1437 return path[:start_deps]
1438 return path
1439
1440 unapproved_dependencies = [
1441 "'+%s'," % StripDeps(path) for path in missing_files
1442 ]
1443
1444 if unapproved_dependencies:
1445 output_list = [
1446 output(
1447 'You need LGTM from owners of depends-on paths in DEPS that '
1448 ' were modified in this CL:\n %s' %
1449 '\n '.join(sorted(unapproved_dependencies)))
1450 ]
1451 suggested_owners = input_api.owners_client.SuggestOwners(
1452 missing_files, exclude=[owner_email])
1453 output_list.append(
1454 output('Suggested missing target path OWNERS:\n %s' %
1455 '\n '.join(suggested_owners or [])))
1456 return output_list
1457
1458 return []