blob: fe7514dc99ce12ad909a49f217882d63def8f9ef [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
Mirko Bonadeid8665442018-09-04 10:17:27136def FindSrcDirPath(starting_dir):
Jeremy Leconte3d476f22023-10-17 13:04:24137 """Returns the abs path to the src/ dir of the project."""
138 src_dir = starting_dir
139 while os.path.basename(src_dir) != 'src':
140 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
141 return src_dir
Mirko Bonadeid8665442018-09-04 10:17:27142
143
Oleh Prypin2f33a562017-10-04 18:17:54144@contextmanager
145def _AddToPath(*paths):
Jeremy Leconte3d476f22023-10-17 13:04:24146 original_sys_path = sys.path
147 sys.path.extend(paths)
148 try:
149 yield
150 finally:
151 # Restore sys.path to what it was before.
152 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 13:27:22153
154
charujain9893e252017-09-14 11:33:22155def VerifyNativeApiHeadersListIsValid(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24156 """Ensures the list of native API header directories is up to date."""
157 non_existing_paths = []
158 native_api_full_paths = [
159 input_api.os_path.join(input_api.PresubmitLocalPath(),
160 *path.split('/')) for path in API_DIRS
Mirko Bonadei8cc66952020-10-30 09:13:45161 ]
Jeremy Leconte3d476f22023-10-17 13:04:24162 for path in native_api_full_paths:
163 if not os.path.isdir(path):
164 non_existing_paths.append(path)
165 if non_existing_paths:
166 return [
167 output_api.PresubmitError(
168 'Directories to native API headers have changed which has made '
169 'the list in PRESUBMIT.py outdated.\nPlease update it to the '
170 'current location of our native APIs.', non_existing_paths)
171 ]
172 return []
kjellander53047c92015-12-03 07:56:14173
Artem Titove92675b2018-05-22 08:21:27174
kjellanderc88b5d52017-04-05 13:42:43175API_CHANGE_MSG = """
kwibergeb133022016-04-07 14:41:48176You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 12:13:23177 1. Make compatible changes that don't break existing clients. Usually
178 this is done by keeping the existing method signatures unchanged.
Danil Chapovalov7013b3b2021-02-22 13:31:26179 2. Mark the old stuff as deprecated (use the ABSL_DEPRECATED macro).
kwibergeb133022016-04-07 14:41:48180 3. Create a timeline and plan for when the deprecated stuff will be
181 removed. (The amount of time we give users to change their code
182 should be informed by how much work it is for them. If they just
183 need to replace one name with another or something equally
184 simple, 1-2 weeks might be good; if they need to do serious work,
185 up to 3 months may be called for.)
186 4. Update/inform existing downstream code owners to stop using the
187 deprecated stuff. (Send announcements to
188 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
189 5. Remove the deprecated stuff, once the agreed-upon amount of time
190 has passed.
191Related files:
192"""
kjellander53047c92015-12-03 07:56:14193
Artem Titove92675b2018-05-22 08:21:27194
charujain9893e252017-09-14 11:33:22195def CheckNativeApiHeaderChanges(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24196 """Checks to remind proper changing of native APIs."""
197 files = []
198 source_file_filter = lambda x: input_api.FilterSourceFile(
199 x, files_to_check=[r'.+\.(gn|gni|h)$'])
200 for f in input_api.AffectedSourceFiles(source_file_filter):
201 for path in API_DIRS:
202 dn = os.path.dirname(f.LocalPath())
203 if path == 'api':
204 # Special case: Subdirectories included.
205 if dn == 'api' or dn.startswith('api/'):
206 files.append(f.LocalPath())
207 else:
208 # Normal case: Subdirectories not included.
209 if dn == path:
210 files.append(f.LocalPath())
kjellander53047c92015-12-03 07:56:14211
Jeremy Leconte3d476f22023-10-17 13:04:24212 if files:
213 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
214 return []
kjellander53047c92015-12-03 07:56:14215
kjellander@webrtc.org0fcaf992015-11-26 14:24:52216
Mirko Bonadei8cc66952020-10-30 09:13:45217def CheckNoIOStreamInHeaders(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24218 """Checks to make sure no .h files include <iostream>."""
219 files = []
220 pattern = input_api.re.compile(r'^#include\s*<iostream>',
221 input_api.re.MULTILINE)
222 file_filter = lambda x: (input_api.FilterSourceFile(x) and
223 source_file_filter(x))
224 for f in input_api.AffectedSourceFiles(file_filter):
225 if not f.LocalPath().endswith('.h'):
226 continue
227 contents = input_api.ReadFile(f)
228 if pattern.search(contents):
229 files.append(f)
kjellander@webrtc.org51198f12012-02-21 17:53:46230
Jeremy Leconte3d476f22023-10-17 13:04:24231 if len(files) > 0:
232 return [
233 output_api.PresubmitError(
234 'Do not #include <iostream> in header files, since it inserts '
235 'static initialization into every file including the header. '
236 'Instead, #include <ostream>. See http://crbug.com/94794',
237 files)
238 ]
239 return []
kjellander@webrtc.org51198f12012-02-21 17:53:46240
kjellander@webrtc.orge4158642014-08-06 09:11:18241
Mirko Bonadei8cc66952020-10-30 09:13:45242def CheckNoPragmaOnce(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24243 """Make sure that banned functions are not used."""
244 files = []
245 pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE)
246 file_filter = lambda x: (input_api.FilterSourceFile(x) and
247 source_file_filter(x))
248 for f in input_api.AffectedSourceFiles(file_filter):
249 if not f.LocalPath().endswith('.h'):
250 continue
251 contents = input_api.ReadFile(f)
252 if pattern.search(contents):
253 files.append(f)
kjellander6aeef742017-02-20 09:13:18254
Jeremy Leconte3d476f22023-10-17 13:04:24255 if files:
256 return [
257 output_api.PresubmitError(
258 'Do not use #pragma once in header files.\n'
259 'See http://www.chromium.org/developers/coding-style'
260 '#TOC-File-headers', files)
261 ]
262 return []
Christoffer Jansson4e8a7732022-02-08 08:01:12263
kjellander6aeef742017-02-20 09:13:18264
Byoungchan Lee94f2ef22021-07-01 13:21:44265def CheckNoFRIEND_TEST(# pylint: disable=invalid-name
Mirko Bonadei8cc66952020-10-30 09:13:45266 input_api,
Byoungchan Lee94f2ef22021-07-01 13:21:44267 output_api,
Mirko Bonadei8cc66952020-10-30 09:13:45268 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24269 """Make sure that gtest's FRIEND_TEST() macro is not used, the
kjellander@webrtc.org51198f12012-02-21 17:53:46270 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
271 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
Jeremy Leconte3d476f22023-10-17 13:04:24272 problems = []
kjellander@webrtc.org51198f12012-02-21 17:53:46273
Jeremy Leconte3d476f22023-10-17 13:04:24274 file_filter = lambda f: (f.LocalPath().endswith(
275 ('.cc', '.h')) and source_file_filter(f))
276 for f in input_api.AffectedFiles(file_filter=file_filter):
277 for line_num, line in f.ChangedContents():
278 if 'FRIEND_TEST(' in line:
279 problems.append(' %s:%d' % (f.LocalPath(), line_num))
kjellander@webrtc.org51198f12012-02-21 17:53:46280
Jeremy Leconte3d476f22023-10-17 13:04:24281 if not problems:
282 return []
283 return [
284 output_api.PresubmitPromptWarning(
285 'WebRTC\'s code should not use gtest\'s FRIEND_TEST() macro. '
286 'Include testsupport/gtest_prod_util.h and use '
287 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))
288 ]
kjellander@webrtc.org51198f12012-02-21 17:53:46289
kjellander@webrtc.orge4158642014-08-06 09:11:18290
Mirko Bonadeifc17a782020-06-30 12:31:37291def IsLintDisabled(disabled_paths, file_path):
Jeremy Leconte3d476f22023-10-17 13:04:24292 """ Checks if a file is disabled for lint check."""
293 for path in disabled_paths:
294 if file_path == path or os.path.dirname(file_path).startswith(path):
295 return True
296 return False
kjellander@webrtc.org0fcaf992015-11-26 14:24:52297
298
charujain9893e252017-09-14 11:33:22299def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 09:23:00300 source_file_filter=None):
Jeremy Leconte3d476f22023-10-17 13:04:24301 """Checks that all new or non-exempt .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 11:33:22302 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46303 depot_tools/presubmit_canned_checks.py but has less filters and only checks
304 added files."""
Jeremy Leconte3d476f22023-10-17 13:04:24305 result = []
kjellander@webrtc.org51198f12012-02-21 17:53:46306
Jeremy Leconte3d476f22023-10-17 13:04:24307 # Initialize cpplint.
308 import cpplint
309 # Access to a protected member _XX of a client class
310 # pylint: disable=W0212
311 cpplint._cpplint_state.ResetErrorCounts()
kjellander@webrtc.org51198f12012-02-21 17:53:46312
Jeremy Leconte3d476f22023-10-17 13:04:24313 lint_filters = cpplint._Filters()
314 lint_filters.extend(DISABLED_LINT_FILTERS)
315 cpplint._SetFilters(','.join(lint_filters))
jbauchc4e3ead2016-02-19 08:25:55316
Jeremy Leconte3d476f22023-10-17 13:04:24317 # Create a platform independent exempt list for cpplint.
318 disabled_paths = [
319 input_api.os_path.join(*path.split('/')) for path in CPPLINT_EXCEPTIONS
320 ]
kjellander@webrtc.org0fcaf992015-11-26 14:24:52321
Jeremy Leconte3d476f22023-10-17 13:04:24322 # Use the strictest verbosity level for cpplint.py (level 1) which is the
323 # default when running cpplint.py from command line. To make it possible to
324 # work with not-yet-converted code, we're only applying it to new (or
325 # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS.
326 verbosity_level = 1
327 files = []
328 for f in input_api.AffectedSourceFiles(source_file_filter):
329 # Note that moved/renamed files also count as added.
330 if f.Action() == 'A' or not IsLintDisabled(disabled_paths,
331 f.LocalPath()):
332 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23333
Jeremy Leconte3d476f22023-10-17 13:04:24334 for file_name in files:
335 cpplint.ProcessFile(file_name, verbosity_level)
kjellander@webrtc.org51198f12012-02-21 17:53:46336
Jeremy Leconte3d476f22023-10-17 13:04:24337 if cpplint._cpplint_state.error_count > 0:
338 if input_api.is_committing:
339 res_type = output_api.PresubmitError
340 else:
341 res_type = output_api.PresubmitPromptWarning
342 result = [res_type('Changelist failed cpplint.py check.')]
kjellander@webrtc.org51198f12012-02-21 17:53:46343
Jeremy Leconte3d476f22023-10-17 13:04:24344 return result
kjellander@webrtc.org51198f12012-02-21 17:53:46345
Artem Titove92675b2018-05-22 08:21:27346
charujain9893e252017-09-14 11:33:22347def CheckNoSourcesAbove(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24348 # Disallow referencing source files with paths above the GN file location.
349 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
350 re.MULTILINE | re.DOTALL)
351 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
352 violating_gn_files = set()
353 violating_source_entries = []
354 for gn_file in gn_files:
355 contents = input_api.ReadFile(gn_file)
356 for source_block_match in source_pattern.finditer(contents):
357 # Find all source list entries starting with ../ in the source block
358 # (exclude overrides entries).
359 for file_list_match in file_pattern.finditer(
360 source_block_match.group(1)):
361 source_file = file_list_match.group(1)
362 if 'overrides/' not in source_file:
363 violating_source_entries.append(source_file)
364 violating_gn_files.add(gn_file)
365 if violating_gn_files:
366 return [
367 output_api.PresubmitError(
368 'Referencing source files above the directory of the GN file '
369 'is not allowed. Please introduce new GN targets in the proper '
370 'location instead.\n'
371 'Invalid source entries:\n'
372 '%s\n'
373 'Violating GN files:' % '\n'.join(violating_source_entries),
374 items=violating_gn_files)
375 ]
376 return []
ehmaldonado5b1ba082016-09-02 12:51:08377
Artem Titove92675b2018-05-22 08:21:27378
Mirko Bonadei4dc4e252017-09-19 11:49:16379def CheckNoMixingSources(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24380 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
Mirko Bonadei4dc4e252017-09-19 11:49:16381
382 See bugs.webrtc.org/7743 for more context.
383 """
Jeremy Leconte3d476f22023-10-17 13:04:24384 def _MoreThanOneSourceUsed(*sources_lists):
385 sources_used = 0
386 for source_list in sources_lists:
387 if len(source_list) > 0:
388 sources_used += 1
389 return sources_used > 1
Artem Titove92675b2018-05-22 08:21:27390
Jeremy Leconte3d476f22023-10-17 13:04:24391 errors = defaultdict(lambda: [])
392 for gn_file in gn_files:
393 gn_file_content = input_api.ReadFile(gn_file)
394 for target_match in TARGET_RE.finditer(gn_file_content):
395 # list_of_sources is a list of tuples of the form
396 # (c_files, cc_files, objc_files) that keeps track of all the
397 # sources defined in a target. A GN target can have more that
398 # on definition of sources (since it supports if/else statements).
399 # E.g.:
400 # rtc_static_library("foo") {
401 # if (is_win) {
402 # sources = [ "foo.cc" ]
403 # } else {
404 # sources = [ "foo.mm" ]
405 # }
406 # }
407 # This is allowed and the presubmit check should support this case.
408 list_of_sources = []
409 c_files = []
410 cc_files = []
411 objc_files = []
412 target_name = target_match.group('target_name')
413 target_contents = target_match.group('target_contents')
414 for sources_match in SOURCES_RE.finditer(target_contents):
415 if '+=' not in sources_match.group(0):
416 if c_files or cc_files or objc_files:
417 list_of_sources.append((c_files, cc_files, objc_files))
418 c_files = []
419 cc_files = []
420 objc_files = []
421 for file_match in FILE_PATH_RE.finditer(
422 sources_match.group(1)):
423 file_path = file_match.group('file_path')
424 extension = file_match.group('extension')
425 if extension == '.c':
426 c_files.append(file_path + extension)
427 if extension == '.cc':
428 cc_files.append(file_path + extension)
429 if extension in ['.m', '.mm']:
430 objc_files.append(file_path + extension)
Mirko Bonadei4dc4e252017-09-19 11:49:16431 list_of_sources.append((c_files, cc_files, objc_files))
Jeremy Leconte3d476f22023-10-17 13:04:24432 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
433 if _MoreThanOneSourceUsed(c_files_list, cc_files_list,
434 objc_files_list):
435 all_sources = sorted(c_files_list + cc_files_list +
436 objc_files_list)
437 errors[gn_file.LocalPath()].append(
438 (target_name, all_sources))
439 if errors:
440 return [
441 output_api.PresubmitError(
442 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
443 'Please create a separate target for each collection of '
444 'sources.\n'
445 'Mixed sources: \n'
446 '%s\n'
447 'Violating GN files:\n%s\n' %
448 (json.dumps(errors, indent=2), '\n'.join(list(errors.keys()))))
449 ]
450 return []
kjellander7439f972016-12-06 06:47:46451
Artem Titove92675b2018-05-22 08:21:27452
charujain9893e252017-09-14 11:33:22453def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24454 cwd = input_api.PresubmitLocalPath()
455 with _AddToPath(
456 input_api.os_path.join(cwd, 'tools_webrtc',
457 'presubmit_checks_lib')):
458 from check_package_boundaries import CheckPackageBoundaries
459 build_files = [
460 os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files
Mirko Bonadei8cc66952020-10-30 09:13:45461 ]
Jeremy Leconte3d476f22023-10-17 13:04:24462 errors = CheckPackageBoundaries(cwd, build_files)[:5]
463 if errors:
464 return [
465 output_api.PresubmitError(
466 'There are package boundary violations in the following GN '
467 'files:',
468 long_text='\n\n'.join(str(err) for err in errors))
469 ]
470 return []
ehmaldonado4fb97462017-01-30 13:27:22471
Mirko Bonadeia51bbd82018-03-08 15:15:45472
Mirko Bonadeif0e0d752018-07-04 06:48:18473def _ReportFileAndLine(filename, line_num):
Jeremy Leconte3d476f22023-10-17 13:04:24474 """Default error formatter for _FindNewViolationsOfRule."""
475 return '%s (line %s)' % (filename, line_num)
Mirko Bonadeia51bbd82018-03-08 15:15:45476
477
Mirko Bonadei8cc66952020-10-30 09:13:45478def CheckNoWarningSuppressionFlagsAreAdded(gn_files,
479 input_api,
480 output_api,
Mirko Bonadeif0e0d752018-07-04 06:48:18481 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24482 """Ensure warning suppression flags are not added without a reason."""
483 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
484 'in WebRTC.\n'
485 'If you are not adding this code (e.g. you are just moving '
486 'existing code) or you want to add an exception,\n'
487 'you can add a comment on the line that causes the problem:\n\n'
488 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
489 '\n'
490 'Affected files:\n')
491 errors = [] # 2-element tuples with (file, line number)
492 clang_warn_re = input_api.re.compile(
493 r'//build/config/clang:extra_warnings')
494 # pylint: disable-next=fixme
495 no_presubmit_re = input_api.re.compile(
496 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
497 for f in gn_files:
498 for line_num, line in f.ChangedContents():
499 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
500 errors.append(error_formatter(f.LocalPath(), line_num))
501 if errors:
502 return [output_api.PresubmitError(msg, errors)]
503 return []
Mirko Bonadeif0e0d752018-07-04 06:48:18504
Mirko Bonadei9ce800d2019-02-05 15:48:13505
Mirko Bonadei8cc66952020-10-30 09:13:45506def CheckNoTestCaseUsageIsAdded(input_api,
507 output_api,
508 source_file_filter,
Mirko Bonadei9ce800d2019-02-05 15:48:13509 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24510 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
511 'new API: https://github.com/google/googletest/blob/master/'
512 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
513 'Affected files:\n')
514 errors = [] # 2-element tuples with (file, line number)
515 test_case_re = input_api.re.compile(r'TEST_CASE')
516 file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith(
517 '.cc'))
518 for f in input_api.AffectedSourceFiles(file_filter):
519 for line_num, line in f.ChangedContents():
520 if test_case_re.search(line):
521 errors.append(error_formatter(f.LocalPath(), line_num))
522 if errors:
523 return [output_api.PresubmitError(error_msg, errors)]
524 return []
Mirko Bonadei9ce800d2019-02-05 15:48:13525
526
Mirko Bonadei8cc66952020-10-30 09:13:45527def CheckNoStreamUsageIsAdded(input_api,
528 output_api,
Artem Titov739351d2018-05-11 10:21:36529 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 06:48:18530 error_formatter=_ReportFileAndLine):
Jeremy Leconte3d476f22023-10-17 13:04:24531 """Make sure that no more dependencies on stringstream are added."""
532 error_msg = (
533 'Usage of <sstream>, <istream> and <ostream> in WebRTC is '
534 'deprecated.\n'
535 'This includes the following types:\n'
536 'std::istringstream, std::ostringstream, std::wistringstream, '
537 'std::wostringstream,\n'
538 'std::wstringstream, std::ostream, std::wostream, std::istream,'
539 'std::wistream,\n'
540 'std::iostream, std::wiostream.\n'
541 'If you are not adding this code (e.g. you are just moving '
542 'existing code),\n'
543 'you can add a comment on the line that causes the problem:\n\n'
544 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
545 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
546 '\n'
547 'If you are adding new code, consider using '
548 'rtc::SimpleStringBuilder\n'
549 '(in rtc_base/strings/string_builder.h).\n'
550 'Affected files:\n')
551 errors = [] # 2-element tuples with (file, line number)
552 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
553 usage_re = input_api.re.compile(
554 r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
555 no_presubmit_re = input_api.re.compile(
556 r'// no-presubmit-check TODO\(webrtc:8982\)')
557 file_filter = lambda x: (input_api.FilterSourceFile(x) and
558 source_file_filter(x))
Mirko Bonadei571791a2019-05-07 12:08:05559
Jeremy Leconte3d476f22023-10-17 13:04:24560 def _IsException(file_path):
561 is_test = any(
562 file_path.endswith(x) for x in
563 ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc'])
564 return (file_path.startswith('examples')
565 or file_path.startswith('test') or is_test)
Patrik Höglund2ea27962020-01-13 14:10:40566
Jeremy Leconte3d476f22023-10-17 13:04:24567 for f in input_api.AffectedSourceFiles(file_filter):
568 # Usage of stringstream is allowed under examples/ and in tests.
569 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
570 continue
571 for line_num, line in f.ChangedContents():
572 if ((include_re.search(line) or usage_re.search(line))
573 and not no_presubmit_re.search(line)):
574 errors.append(error_formatter(f.LocalPath(), line_num))
575 if errors:
576 return [output_api.PresubmitError(error_msg, errors)]
577 return []
Mirko Bonadeia51bbd82018-03-08 15:15:45578
Artem Titove92675b2018-05-22 08:21:27579
Mirko Bonadeia05d47e2018-05-09 09:03:38580def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24581 """Checks that public_deps is not used without a good reason."""
582 result = []
583 no_presubmit_check_re = input_api.re.compile(
584 r'# no-presubmit-check TODO\(webrtc:\d+\)')
585 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
586 'because it doesn\'t map well to downstream build systems.\n'
587 'Used in: %s (line %d).\n'
588 'If you are not adding this code (e.g. you are just moving '
589 'existing code) or you have a good reason, you can add this '
590 'comment (verbatim) on the line that causes the problem:\n\n'
591 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
592 for affected_file in gn_files:
593 for (line_number, affected_line) in affected_file.ChangedContents():
594 if 'public_deps' in affected_line:
595 surpressed = no_presubmit_check_re.search(affected_line)
596 if not surpressed:
597 result.append(
598 output_api.PresubmitError(
599 error_msg %
600 (affected_file.LocalPath(), line_number)))
601 return result
Mirko Bonadei5c1ad592017-12-12 10:52:27602
Artem Titove92675b2018-05-22 08:21:27603
Mirko Bonadei05691dd2019-10-22 14:34:24604def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24605 result = []
606 error_msg = (
607 'check_includes overrides are not allowed since it can cause '
608 'incorrect dependencies to form. It effectively means that your '
609 'module can include any .h file without depending on its '
610 'corresponding target. There are some exceptional cases when '
611 'this is allowed: if so, get approval from a .gn owner in the '
612 'root OWNERS file.\n'
613 'Used in: %s (line %d).')
614 # pylint: disable-next=fixme
615 no_presubmit_re = input_api.re.compile(
616 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
617 for affected_file in gn_files:
618 for (line_number, affected_line) in affected_file.ChangedContents():
619 if ('check_includes' in affected_line
620 and not no_presubmit_re.search(affected_line)):
621 result.append(
622 output_api.PresubmitError(
623 error_msg % (affected_file.LocalPath(), line_number)))
624 return result
Patrik Höglund6f491062018-01-11 11:04:23625
Artem Titove92675b2018-05-22 08:21:27626
Mirko Bonadeif0e0d752018-07-04 06:48:18627def CheckGnChanges(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24628 file_filter = lambda x: (input_api.FilterSourceFile(
629 x,
630 files_to_check=(r'.+\.(gn|gni)$', ),
631 files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', )))
ehmaldonado5b1ba082016-09-02 12:51:08632
Jeremy Leconte3d476f22023-10-17 13:04:24633 gn_files = []
634 for f in input_api.AffectedSourceFiles(file_filter):
635 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 12:51:08636
Jeremy Leconte3d476f22023-10-17 13:04:24637 result = []
638 if gn_files:
639 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
640 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
Jeremy Leconte3d476f22023-10-17 13:04:24641 result.extend(
642 CheckNoPackageBoundaryViolations(input_api, gn_files, output_api))
643 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api,
644 output_api))
645 result.extend(
646 CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
647 result.extend(
648 CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
649 output_api))
650 return result
ehmaldonado5b1ba082016-09-02 12:51:08651
Artem Titove92675b2018-05-22 08:21:27652
Oleh Prypin920b6532017-10-05 09:28:51653def CheckGnGen(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24654 """Runs `gn gen --check` with default args to detect mismatches between
Oleh Prypin920b6532017-10-05 09:28:51655 #includes and dependencies in the BUILD.gn files, as well as general build
656 errors.
657 """
Jeremy Leconte3d476f22023-10-17 13:04:24658 with _AddToPath(
659 input_api.os_path.join(input_api.PresubmitLocalPath(),
660 'tools_webrtc', 'presubmit_checks_lib')):
661 from build_helpers import RunGnCheck
662 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
663 if errors:
664 return [
665 output_api.PresubmitPromptWarning(
666 'Some #includes do not match the build dependency graph. '
667 'Please run:\n'
668 ' gn gen --check <out_dir>',
669 long_text='\n\n'.join(errors))
670 ]
671 return []
Oleh Prypin920b6532017-10-05 09:28:51672
Artem Titove92675b2018-05-22 08:21:27673
Artem Titova04d1402018-05-11 09:23:00674def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24675 """Runs checkdeps on #include statements added in this
kjellander@webrtc.org3bd41562014-09-01 11:06:37676 change. Breaking - rules is an error, breaking ! rules is a
677 warning.
678 """
Jeremy Leconte3d476f22023-10-17 13:04:24679 # Copied from Chromium's src/PRESUBMIT.py.
kjellander@webrtc.org3bd41562014-09-01 11:06:37680
Jeremy Leconte3d476f22023-10-17 13:04:24681 # We need to wait until we have an input_api object and use this
682 # roundabout construct to import checkdeps because this file is
683 # eval-ed and thus doesn't have __file__.
684 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
685 checkdeps_path = input_api.os_path.join(src_path, 'buildtools',
686 'checkdeps')
687 if not os.path.exists(checkdeps_path):
688 return [
689 output_api.PresubmitError(
690 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
691 'download all the DEPS entries?' % checkdeps_path)
692 ]
693 with _AddToPath(checkdeps_path):
694 import checkdeps
695 from cpp_checker import CppChecker
696 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37697
Jeremy Leconte3d476f22023-10-17 13:04:24698 added_includes = []
699 for f in input_api.AffectedFiles(file_filter=source_file_filter):
700 if not CppChecker.IsCppFile(f.LocalPath()):
701 continue
kjellander@webrtc.org3bd41562014-09-01 11:06:37702
Jeremy Leconte3d476f22023-10-17 13:04:24703 changed_lines = [line for _, line in f.ChangedContents()]
704 added_includes.append([f.LocalPath(), changed_lines])
kjellander@webrtc.org3bd41562014-09-01 11:06:37705
Jeremy Leconte3d476f22023-10-17 13:04:24706 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
kjellander@webrtc.org3bd41562014-09-01 11:06:37707
Jeremy Leconte3d476f22023-10-17 13:04:24708 error_descriptions = []
709 warning_descriptions = []
710 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
711 added_includes):
712 description_with_path = '%s\n %s' % (path, rule_description)
713 if rule_type == Rule.DISALLOW:
714 error_descriptions.append(description_with_path)
715 else:
716 warning_descriptions.append(description_with_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37717
Jeremy Leconte3d476f22023-10-17 13:04:24718 results = []
719 if error_descriptions:
720 results.append(
721 output_api.PresubmitError(
722 'You added one or more #includes that violate checkdeps rules.'
723 '\nCheck that the DEPS files in these locations contain valid '
724 'rules.\nSee '
725 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
726 'for more details about checkdeps.', error_descriptions))
727 if warning_descriptions:
728 results.append(
729 output_api.PresubmitPromptOrNotify(
730 'You added one or more #includes of files that are temporarily'
731 '\nallowed but being removed. Can you avoid introducing the\n'
732 '#include? See relevant DEPS file(s) for details and contacts.'
733 '\nSee '
734 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
735 'for more details about checkdeps.', warning_descriptions))
736 return results
kjellander@webrtc.org3bd41562014-09-01 11:06:37737
Artem Titove92675b2018-05-22 08:21:27738
charujain9893e252017-09-14 11:33:22739def CheckCommitMessageBugEntry(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24740 """Check that bug entries are well-formed in commit message."""
741 bogus_bug_msg = (
742 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
743 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.'
744 )
745 results = []
746 for bug in input_api.change.BugsFromDescription():
747 bug = bug.strip()
748 if bug.lower() == 'none':
749 continue
750 if 'b/' not in bug and ':' not in bug:
751 try:
752 if int(bug) > 100000:
753 # Rough indicator for current chromium bugs.
754 prefix_guess = 'chromium'
755 else:
756 prefix_guess = 'webrtc'
757 results.append(
758 'Bug entry requires issue tracker prefix, e.g. %s:%s' %
759 (prefix_guess, bug))
760 except ValueError:
761 results.append(bogus_bug_msg % bug)
762 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
763 results.append(bogus_bug_msg % bug)
764 return [output_api.PresubmitError(r) for r in results]
charujain9893e252017-09-14 11:33:22765
Artem Titove92675b2018-05-22 08:21:27766
charujain9893e252017-09-14 11:33:22767def CheckChangeHasBugField(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24768 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 15:11:16769
770 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 13:12:35771 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 15:11:16772 a bug reference.
Mirko Bonadei61880182017-10-12 13:12:35773
774 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
775 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 15:11:16776 """
Jeremy Leconte3d476f22023-10-17 13:04:24777 if input_api.change.BugsFromDescription():
778 return []
779 return [
780 output_api.PresubmitError(
781 'The "Bug: [bug number]" footer is mandatory. Please create a '
782 'bug and reference it using either of:\n'
783 ' * https://bugs.webrtc.org - reference it using Bug: '
784 'webrtc:XXXX\n'
785 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')
786 ]
kjellander@webrtc.orge4158642014-08-06 09:11:18787
Artem Titove92675b2018-05-22 08:21:27788
Artem Titova04d1402018-05-11 09:23:00789def CheckJSONParseErrors(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24790 """Check that JSON files do not contain syntax errors."""
791 def FilterFile(affected_file):
792 return (input_api.os_path.splitext(affected_file.LocalPath())[1]
793 == '.json' and source_file_filter(affected_file))
kjellander569cf942016-02-11 13:02:59794
Jeremy Leconte3d476f22023-10-17 13:04:24795 def GetJSONParseError(input_api, filename):
796 try:
797 contents = input_api.ReadFile(filename)
798 input_api.json.loads(contents)
799 except ValueError as e:
800 return e
801 return None
kjellander569cf942016-02-11 13:02:59802
Jeremy Leconte3d476f22023-10-17 13:04:24803 results = []
804 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
805 include_deletes=False):
806 parse_error = GetJSONParseError(input_api,
807 affected_file.AbsoluteLocalPath())
808 if parse_error:
809 results.append(
810 output_api.PresubmitError(
811 '%s could not be parsed: %s' %
812 (affected_file.LocalPath(), parse_error)))
813 return results
kjellander569cf942016-02-11 13:02:59814
815
charujain9893e252017-09-14 11:33:22816def RunPythonTests(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24817 def Join(*args):
818 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
Henrik Kjellander8d3ad822015-05-26 17:52:05819
Jeremy Leconte3d476f22023-10-17 13:04:24820 excluded_files = [
821 # These tests should be run manually after webrtc_dashboard_upload
822 # target has been built.
823 'catapult_uploader_test.py',
824 'process_perf_results_test.py',
825 ]
Christoffer Jansson70098a82022-02-21 18:43:36826
Jeremy Leconte3d476f22023-10-17 13:04:24827 test_directories = [
828 input_api.PresubmitLocalPath(),
829 Join('rtc_tools', 'py_event_log_analyzer'),
830 ] + [
831 root for root, _, files in os.walk(Join('tools_webrtc')) if any(
832 f.endswith('_test.py') and f not in excluded_files for f in files)
833 ]
Henrik Kjellander8d3ad822015-05-26 17:52:05834
Jeremy Leconte3d476f22023-10-17 13:04:24835 tests = []
Christoffer Jansson1b083a92022-02-15 13:52:31836
Jeremy Leconte3d476f22023-10-17 13:04:24837 for directory in test_directories:
838 tests.extend(
839 input_api.canned_checks.GetUnitTestsInDirectory(
840 input_api,
841 output_api,
842 directory,
843 files_to_check=[r'.+_test\.py$'],
844 run_on_python2=False))
845 return input_api.RunTests(tests, parallel=True)
Henrik Kjellander8d3ad822015-05-26 17:52:05846
847
Artem Titova04d1402018-05-11 09:23:00848def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
849 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:24850 """Checks that the namespace google::protobuf has not been used."""
851 files = []
852 pattern = input_api.re.compile(r'google::protobuf')
853 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
854 file_filter = lambda x: (input_api.FilterSourceFile(x) and
855 source_file_filter(x))
856 for f in input_api.AffectedSourceFiles(file_filter):
857 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
858 continue
859 contents = input_api.ReadFile(f)
860 if pattern.search(contents):
861 files.append(f)
mbonadei38415b22017-04-07 12:38:01862
Jeremy Leconte3d476f22023-10-17 13:04:24863 if files:
864 return [
865 output_api.PresubmitError(
866 'Please avoid to use namespace `google::protobuf` directly.\n'
867 'Add a using directive in `%s` and include that header instead.'
868 % proto_utils_path, files)
869 ]
870 return []
mbonadei38415b22017-04-07 12:38:01871
872
Mirko Bonadei92ea95e2017-09-15 04:47:31873def _LicenseHeader(input_api):
Jeremy Leconte3d476f22023-10-17 13:04:24874 """Returns the license header regexp."""
875 # Accept any year number from 2003 to the current year
876 current_year = int(input_api.time.strftime('%Y'))
877 allowed_years = (str(s) for s in reversed(range(2003, current_year + 1)))
878 years_re = '(' + '|'.join(allowed_years) + ')'
879 license_header = (
880 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
881 r'All [Rr]ights [Rr]eserved\.\n'
882 r'.*?\n'
883 r'.*? Use of this source code is governed by a BSD-style license\n'
884 r'.*? that can be found in the LICENSE file in the root of the source\n'
885 r'.*? tree\. An additional intellectual property rights grant can be '
886 r'found\n'
887 r'.*? in the file PATENTS\. All contributing project authors may\n'
888 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
889 ) % {
890 'year': years_re,
891 }
892 return license_header
Mirko Bonadei92ea95e2017-09-15 04:47:31893
894
charujain9893e252017-09-14 11:33:22895def CommonChecks(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:24896 """Checks common to both upload and commit."""
897 results = []
898 # Filter out files that are in objc or ios dirs from being cpplint-ed since
899 # they do not follow C++ lint rules.
900 exception_list = input_api.DEFAULT_FILES_TO_SKIP + (
901 r".*\bobjc[\\\/].*",
902 r".*objc\.[hcm]+$",
903 )
904 source_file_filter = lambda x: input_api.FilterSourceFile(
905 x, None, exception_list)
906 results.extend(
907 CheckApprovedFilesLintClean(input_api, output_api, source_file_filter))
908 results.extend(
909 input_api.canned_checks.CheckLicense(input_api, output_api,
910 _LicenseHeader(input_api)))
Byoungchan Lee94f2ef22021-07-01 13:21:44911
Jeremy Leconte3d476f22023-10-17 13:04:24912 # TODO(bugs.webrtc.org/12114): Delete this filter and run pylint on
913 # all python files. This is a temporary solution.
914 python_file_filter = lambda f: (f.LocalPath().endswith('.py') and
915 source_file_filter(f))
916 python_changed_files = [
917 f.LocalPath()
918 for f in input_api.AffectedFiles(include_deletes=False,
919 file_filter=python_file_filter)
920 ]
921 pylint_new_style = [
922 f for f in python_changed_files if f not in PYLINT_OLD_STYLE
923 ]
924 pylint_old_style = [
925 f for f in python_changed_files if f in PYLINT_OLD_STYLE
926 ]
927 if pylint_new_style:
928 results.extend(
929 input_api.canned_checks.RunPylint(
930 input_api,
931 output_api,
932 files_to_check=pylint_new_style,
933 files_to_skip=(
934 r'^base[\\\/].*\.py$',
935 r'^build[\\\/].*\.py$',
936 r'^buildtools[\\\/].*\.py$',
937 r'^infra[\\\/].*\.py$',
938 r'^ios[\\\/].*\.py$',
939 r'^out.*[\\\/].*\.py$',
940 r'^testing[\\\/].*\.py$',
941 r'^third_party[\\\/].*\.py$',
942 r'^tools[\\\/].*\.py$',
943 r'^xcodebuild.*[\\\/].*\.py$',
944 ),
945 pylintrc='pylintrc',
946 version='2.7'))
Byoungchan Lee94f2ef22021-07-01 13:21:44947
Jeremy Leconte3d476f22023-10-17 13:04:24948 if pylint_old_style:
949 results.extend(
950 input_api.canned_checks.RunPylint(input_api,
951 output_api,
952 files_to_check=pylint_old_style,
953 pylintrc='pylintrc_old_style',
954 version='2.7'))
955 # TODO(bugs.webrtc.org/13606): talk/ is no more, so make below checks
956 # simpler. WebRTC can't use the presubmit_canned_checks.PanProjectChecks
957 # function since we need to have different license checks in talk/ and
958 # webrtc/directories. Instead, hand-picked checks are included below.
kjellander569cf942016-02-11 13:02:59959
Jeremy Leconte3d476f22023-10-17 13:04:24960 # .m and .mm files are ObjC files. For simplicity we will consider
961 # .h files in ObjC subdirectories ObjC headers.
962 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
963 # Skip long-lines check for DEPS and GN files.
964 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
965 # Also we will skip most checks for third_party directory.
966 third_party_filter_list = (r'(^|.*[\\\/])third_party[\\\/].+', )
967 eighty_char_sources = lambda x: input_api.FilterSourceFile(
968 x,
969 files_to_skip=build_file_filter_list + objc_filter_list +
970 third_party_filter_list)
971 hundred_char_sources = lambda x: input_api.FilterSourceFile(
972 x, files_to_check=objc_filter_list)
973 non_third_party_sources = lambda x: input_api.FilterSourceFile(
974 x, files_to_skip=third_party_filter_list)
Henrik Kjellander63224672015-09-08 06:03:56975
Jeremy Leconte3d476f22023-10-17 13:04:24976 results.extend(
977 input_api.canned_checks.CheckLongLines(
978 input_api,
979 output_api,
980 maxlen=80,
981 source_file_filter=eighty_char_sources))
982 results.extend(
983 input_api.canned_checks.CheckLongLines(
984 input_api,
985 output_api,
986 maxlen=100,
987 source_file_filter=hundred_char_sources))
988 results.extend(
989 input_api.canned_checks.CheckChangeHasNoTabs(
990 input_api, output_api, source_file_filter=non_third_party_sources))
991 results.extend(
992 input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
993 input_api, output_api, source_file_filter=non_third_party_sources))
994 results.extend(
995 input_api.canned_checks.CheckAuthorizedAuthor(
996 input_api,
997 output_api,
998 bot_allowlist=[
999 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com',
1000 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com',
1001 ]))
1002 results.extend(
1003 input_api.canned_checks.CheckChangeTodoHasOwner(
1004 input_api, output_api, source_file_filter=non_third_party_sources))
1005 results.extend(
1006 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
1007 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
1008 results.extend(
1009 CheckNoIOStreamInHeaders(input_api,
1010 output_api,
1011 source_file_filter=non_third_party_sources))
1012 results.extend(
1013 CheckNoPragmaOnce(input_api,
1014 output_api,
1015 source_file_filter=non_third_party_sources))
1016 results.extend(
1017 CheckNoFRIEND_TEST(input_api,
Mirko Bonadei8cc66952020-10-30 09:13:451018 output_api,
1019 source_file_filter=non_third_party_sources))
Jeremy Leconte3d476f22023-10-17 13:04:241020 results.extend(CheckGnChanges(input_api, output_api))
1021 results.extend(
1022 CheckUnwantedDependencies(input_api,
1023 output_api,
1024 source_file_filter=non_third_party_sources))
1025 results.extend(
1026 CheckJSONParseErrors(input_api,
1027 output_api,
1028 source_file_filter=non_third_party_sources))
1029 results.extend(RunPythonTests(input_api, output_api))
1030 results.extend(
1031 CheckUsageOfGoogleProtobufNamespace(
1032 input_api, output_api, source_file_filter=non_third_party_sources))
1033 results.extend(
1034 CheckOrphanHeaders(input_api,
1035 output_api,
1036 source_file_filter=non_third_party_sources))
1037 results.extend(
1038 CheckNewlineAtTheEndOfProtoFiles(
1039 input_api, output_api, source_file_filter=non_third_party_sources))
1040 results.extend(
Byoungchan Leeeb76f192024-01-22 16:55:141041 CheckLFNewline(input_api, output_api, non_third_party_sources))
1042 results.extend(
Jeremy Leconte3d476f22023-10-17 13:04:241043 CheckNoStreamUsageIsAdded(input_api, output_api,
Mirko Bonadei8cc66952020-10-30 09:13:451044 non_third_party_sources))
Jeremy Leconte3d476f22023-10-17 13:04:241045 results.extend(
1046 CheckNoTestCaseUsageIsAdded(input_api, output_api,
1047 non_third_party_sources))
1048 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1049 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
1050 results.extend(
1051 CheckAbslMemoryInclude(input_api, output_api, non_third_party_sources))
1052 results.extend(
1053 CheckAssertUsage(input_api, output_api, non_third_party_sources))
1054 results.extend(
1055 CheckBannedAbslMakeUnique(input_api, output_api,
1056 non_third_party_sources))
1057 results.extend(
1058 CheckObjcApiSymbols(input_api, output_api, non_third_party_sources))
1059 return results
Mirko Bonadeia418e672018-10-24 11:57:251060
1061
1062def CheckApiDepsFileIsUpToDate(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241063 """Check that 'include_rules' in api/DEPS is up to date.
Mirko Bonadei90490372018-10-26 11:17:471064
1065 The file api/DEPS must be kept up to date in order to avoid to avoid to
1066 include internal header from WebRTC's api/ headers.
1067
1068 This check is focused on ensuring that 'include_rules' contains a deny
1069 rule for each root level directory. More focused allow rules can be
1070 added to 'specific_include_rules'.
1071 """
Jeremy Leconte3d476f22023-10-17 13:04:241072 results = []
1073 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
1074 with open(api_deps) as f:
1075 deps_content = _ParseDeps(f.read())
Mirko Bonadeia418e672018-10-24 11:57:251076
Jeremy Leconte3d476f22023-10-17 13:04:241077 include_rules = deps_content.get('include_rules', [])
1078 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 11:57:251079
Jeremy Leconte3d476f22023-10-17 13:04:241080 # Only check top level directories affected by the current CL.
1081 dirs_to_check = set()
1082 for f in input_api.AffectedFiles():
1083 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
1084 if len(path_tokens) > 1:
1085 if (path_tokens[0] not in dirs_to_skip and os.path.isdir(
1086 os.path.join(input_api.PresubmitLocalPath(),
1087 path_tokens[0]))):
1088 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 11:57:251089
Jeremy Leconte3d476f22023-10-17 13:04:241090 missing_include_rules = set()
1091 for p in dirs_to_check:
1092 rule = '-%s' % p
1093 if rule not in include_rules:
1094 missing_include_rules.add(rule)
Mirko Bonadei90490372018-10-26 11:17:471095
Jeremy Leconte3d476f22023-10-17 13:04:241096 if missing_include_rules:
1097 error_msg = [
1098 'include_rules = [\n',
1099 ' ...\n',
1100 ]
Mirko Bonadeia418e672018-10-24 11:57:251101
Jeremy Leconte3d476f22023-10-17 13:04:241102 for r in sorted(missing_include_rules):
1103 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 11:57:251104
Jeremy Leconte3d476f22023-10-17 13:04:241105 error_msg.append(' ...\n')
1106 error_msg.append(']\n')
Mirko Bonadei90490372018-10-26 11:17:471107
Jeremy Leconte3d476f22023-10-17 13:04:241108 results.append(
1109 output_api.PresubmitError(
1110 'New root level directory detected! WebRTC api/ headers should '
1111 'not #include headers from \n'
1112 'the new directory, so please update "include_rules" in file\n'
1113 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
Mirko Bonadei90490372018-10-26 11:17:471114
Jeremy Leconte3d476f22023-10-17 13:04:241115 return results
Mirko Bonadei8cc66952020-10-30 09:13:451116
andrew@webrtc.org2442de12012-01-23 17:45:411117
Mirko Bonadei9fa8ef12019-09-17 17:14:131118def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241119 file_filter = lambda f: (f.LocalPath().endswith(
1120 ('.cc', '.h')) and source_file_filter(f))
Mirko Bonadei9fa8ef12019-09-17 17:14:131121
Jeremy Leconte3d476f22023-10-17 13:04:241122 files = []
1123 for f in input_api.AffectedFiles(include_deletes=False,
1124 file_filter=file_filter):
1125 for _, line in f.ChangedContents():
1126 if 'absl::make_unique' in line:
1127 files.append(f)
1128 break
Mirko Bonadei9fa8ef12019-09-17 17:14:131129
Jeremy Leconte3d476f22023-10-17 13:04:241130 if files:
1131 return [
1132 output_api.PresubmitError(
1133 'Please use std::make_unique instead of absl::make_unique.\n'
1134 'Affected files:', files)
1135 ]
1136 return []
Mirko Bonadei8cc66952020-10-30 09:13:451137
Mirko Bonadei9fa8ef12019-09-17 17:14:131138
Mirko Bonadeid74c0e62020-07-16 19:57:011139def CheckObjcApiSymbols(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241140 rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}',
1141 re.MULTILINE | re.DOTALL)
1142 file_filter = lambda f: (f.LocalPath().endswith(
1143 ('.h')) and source_file_filter(f))
Mirko Bonadeid74c0e62020-07-16 19:57:011144
Jeremy Leconte3d476f22023-10-17 13:04:241145 files = []
1146 file_filter = lambda x: (input_api.FilterSourceFile(x) and
1147 source_file_filter(x))
1148 for f in input_api.AffectedSourceFiles(file_filter):
1149 if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath():
1150 continue
1151 if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'):
1152 continue
1153 contents = input_api.ReadFile(f)
1154 for match in rtc_objc_export.finditer(contents):
1155 export_block = match.group(0)
1156 if 'RTC_OBJC_TYPE' not in export_block:
1157 files.append(f.LocalPath())
Mirko Bonadeid74c0e62020-07-16 19:57:011158
Jeremy Leconte3d476f22023-10-17 13:04:241159 if len(files) > 0:
1160 return [
1161 output_api.PresubmitError(
1162 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() '
1163 + 'macro.\n\n' + 'For example:\n' +
1164 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' +
1165 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' +
1166 'Please fix the following files:', files)
1167 ]
1168 return []
Mirko Bonadei8cc66952020-10-30 09:13:451169
Mirko Bonadeid74c0e62020-07-16 19:57:011170
Mirko Bonadeia6395132021-07-22 15:35:591171def CheckAssertUsage(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241172 pattern = input_api.re.compile(r'\bassert\(')
1173 file_filter = lambda f: (f.LocalPath().endswith(
1174 ('.cc', '.h', '.m', '.mm')) and source_file_filter(f))
Mirko Bonadeia6395132021-07-22 15:35:591175
Jeremy Leconte3d476f22023-10-17 13:04:241176 files = []
1177 for f in input_api.AffectedFiles(include_deletes=False,
1178 file_filter=file_filter):
1179 for _, line in f.ChangedContents():
1180 if pattern.search(line):
1181 files.append(f.LocalPath())
1182 break
Mirko Bonadeia6395132021-07-22 15:35:591183
Jeremy Leconte3d476f22023-10-17 13:04:241184 if len(files) > 0:
1185 return [
1186 output_api.PresubmitError(
1187 'Usage of assert() has been detected in the following files, '
1188 'please use RTC_DCHECK() instead.\n Files:', files)
1189 ]
1190 return []
Mirko Bonadeia6395132021-07-22 15:35:591191
1192
tzika06bf852018-11-15 11:37:351193def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241194 pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"',
1195 input_api.re.MULTILINE)
1196 file_filter = lambda f: (f.LocalPath().endswith(
1197 ('.cc', '.h')) and source_file_filter(f))
tzika06bf852018-11-15 11:37:351198
Jeremy Leconte3d476f22023-10-17 13:04:241199 files = []
1200 for f in input_api.AffectedFiles(include_deletes=False,
1201 file_filter=file_filter):
1202 contents = input_api.ReadFile(f)
1203 if pattern.search(contents):
1204 continue
1205 for _, line in f.ChangedContents():
1206 if 'absl::WrapUnique' in line:
1207 files.append(f)
1208 break
tzika06bf852018-11-15 11:37:351209
Jeremy Leconte3d476f22023-10-17 13:04:241210 if len(files) > 0:
1211 return [
1212 output_api.PresubmitError(
1213 'Please include "absl/memory/memory.h" header for '
1214 'absl::WrapUnique.\nThis header may or may not be included '
1215 'transitively depending on the C++ standard version.', files)
1216 ]
1217 return []
Mirko Bonadei8cc66952020-10-30 09:13:451218
kjellander@webrtc.orge4158642014-08-06 09:11:181219
andrew@webrtc.org53df1362012-01-26 21:24:231220def CheckChangeOnUpload(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241221 results = []
1222 results.extend(CommonChecks(input_api, output_api))
1223 results.extend(CheckGnGen(input_api, output_api))
1224 results.extend(
1225 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
1226 return results
niklase@google.comda159d62011-05-30 11:51:341227
kjellander@webrtc.orge4158642014-08-06 09:11:181228
andrew@webrtc.org2442de12012-01-23 17:45:411229def CheckChangeOnCommit(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241230 results = []
1231 results.extend(CommonChecks(input_api, output_api))
1232 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
1233 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
1234 results.extend(
1235 input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api))
1236 results.extend(
1237 input_api.canned_checks.CheckChangeHasDescription(
1238 input_api, output_api))
1239 results.extend(CheckChangeHasBugField(input_api, output_api))
1240 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
1241 results.extend(
1242 input_api.canned_checks.CheckTreeIsOpen(
1243 input_api,
1244 output_api,
1245 json_url='http://webrtc-status.appspot.com/current?format=json'))
1246 return results
mbonadei74973ed2017-05-09 14:58:051247
1248
Artem Titova04d1402018-05-11 09:23:001249def CheckOrphanHeaders(input_api, output_api, source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241250 # We need to wait until we have an input_api object and use this
1251 # roundabout construct to import prebubmit_checks_lib because this file is
1252 # eval-ed and thus doesn't have __file__.
1253 error_msg = """{} should be listed in {}."""
1254 results = []
1255 exempt_paths = [re.escape(os.path.join('tools_webrtc', 'ios', 'SDK'))]
Christoffer Jansson884e8ae2022-02-11 20:29:381256
Jeremy Leconte3d476f22023-10-17 13:04:241257 with _AddToPath(
1258 input_api.os_path.join(input_api.PresubmitLocalPath(),
1259 'tools_webrtc', 'presubmit_checks_lib')):
1260 from check_orphan_headers import GetBuildGnPathFromFilePath
1261 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 14:58:051262
Jeremy Leconte3d476f22023-10-17 13:04:241263 file_filter = lambda x: input_api.FilterSourceFile(
1264 x, files_to_skip=exempt_paths) and source_file_filter(x)
1265 for f in input_api.AffectedSourceFiles(file_filter):
1266 if f.LocalPath().endswith('.h'):
1267 file_path = os.path.abspath(f.LocalPath())
1268 root_dir = os.getcwd()
1269 gn_file_path = GetBuildGnPathFromFilePath(file_path,
1270 os.path.exists, root_dir)
1271 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1272 if not in_build_gn:
1273 results.append(
1274 output_api.PresubmitError(
1275 error_msg.format(f.LocalPath(),
1276 os.path.relpath(gn_file_path))))
1277 return results
Mirko Bonadei960fd5b2017-06-29 12:59:361278
1279
Mirko Bonadei8cc66952020-10-30 09:13:451280def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api,
1281 source_file_filter):
Jeremy Leconte3d476f22023-10-17 13:04:241282 """Checks that all .proto files are terminated with a newline."""
1283 error_msg = 'File {} must end with exactly one newline.'
1284 results = []
1285 file_filter = lambda x: input_api.FilterSourceFile(
1286 x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x)
1287 for f in input_api.AffectedSourceFiles(file_filter):
1288 file_path = f.LocalPath()
1289 with open(file_path) as f:
1290 lines = f.readlines()
1291 if len(lines) > 0 and not lines[-1].endswith('\n'):
1292 results.append(
1293 output_api.PresubmitError(error_msg.format(file_path)))
1294 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231295
1296
Byoungchan Leeeb76f192024-01-22 16:55:141297def CheckLFNewline(input_api, output_api, source_file_filter):
1298 """Checks that all files have LF newlines."""
1299 error_msg = 'File {} must use LF newlines.'
1300 results = []
1301 file_filter = lambda x: input_api.FilterSourceFile(
1302 x, files_to_check=(r'.+', )) and source_file_filter(x)
1303 for f in input_api.AffectedSourceFiles(file_filter):
1304 file_path = f.LocalPath()
1305 with open(file_path, 'rb') as f:
1306 if b'\r\n' in f.read():
1307 results.append(
1308 output_api.PresubmitError(error_msg.format(file_path)))
1309 return results
1310
Mirko Bonadei7e4ee6e2018-09-28 09:45:231311def _ExtractAddRulesFromParsedDeps(parsed_deps):
Jeremy Leconte3d476f22023-10-17 13:04:241312 """Extract the rules that add dependencies from a parsed DEPS file.
Mirko Bonadei7e4ee6e2018-09-28 09:45:231313
1314 Args:
1315 parsed_deps: the locals dictionary from evaluating the DEPS file."""
Jeremy Leconte3d476f22023-10-17 13:04:241316 add_rules = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231317 add_rules.update([
Jeremy Leconte3d476f22023-10-17 13:04:241318 rule[1:] for rule in parsed_deps.get('include_rules', [])
Mirko Bonadei7e4ee6e2018-09-28 09:45:231319 if rule.startswith('+') or rule.startswith('!')
1320 ])
Jeremy Leconte3d476f22023-10-17 13:04:241321 for _, rules in parsed_deps.get('specific_include_rules', {}).items():
1322 add_rules.update([
1323 rule[1:] for rule in rules
1324 if rule.startswith('+') or rule.startswith('!')
1325 ])
1326 return add_rules
Mirko Bonadei7e4ee6e2018-09-28 09:45:231327
1328
1329def _ParseDeps(contents):
Jeremy Leconte3d476f22023-10-17 13:04:241330 """Simple helper for parsing DEPS files."""
Mirko Bonadei7e4ee6e2018-09-28 09:45:231331
Jeremy Leconte3d476f22023-10-17 13:04:241332 # Stubs for handling special syntax in the root DEPS file.
1333 class VarImpl:
1334 def __init__(self, local_scope):
1335 self._local_scope = local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231336
Jeremy Leconte3d476f22023-10-17 13:04:241337 def Lookup(self, var_name):
1338 """Implements the Var syntax."""
1339 try:
1340 return self._local_scope['vars'][var_name]
1341 except KeyError as var_not_defined:
1342 raise Exception('Var is not defined: %s' %
1343 var_name) from var_not_defined
Mirko Bonadei7e4ee6e2018-09-28 09:45:231344
Jeremy Leconte3d476f22023-10-17 13:04:241345 local_scope = {}
1346 global_scope = {
1347 'Var': VarImpl(local_scope).Lookup,
1348 }
1349 exec(contents, global_scope, local_scope)
1350 return local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231351
1352
1353def _CalculateAddedDeps(os_path, old_contents, new_contents):
Jeremy Leconte3d476f22023-10-17 13:04:241354 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
Mirko Bonadei7e4ee6e2018-09-28 09:45:231355 a set of DEPS entries that we should look up.
1356
1357 For a directory (rather than a specific filename) we fake a path to
1358 a specific filename by adding /DEPS. This is chosen as a file that
1359 will seldom or never be subject to per-file include_rules.
1360 """
Jeremy Leconte3d476f22023-10-17 13:04:241361 # We ignore deps entries on auto-generated directories.
1362 auto_generated_dirs = ['grit', 'jni']
Mirko Bonadei7e4ee6e2018-09-28 09:45:231363
Jeremy Leconte3d476f22023-10-17 13:04:241364 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1365 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231366
Jeremy Leconte3d476f22023-10-17 13:04:241367 added_deps = new_deps.difference(old_deps)
Mirko Bonadei7e4ee6e2018-09-28 09:45:231368
Jeremy Leconte3d476f22023-10-17 13:04:241369 results = set()
1370 for added_dep in added_deps:
1371 if added_dep.split('/')[0] in auto_generated_dirs:
1372 continue
1373 # Assume that a rule that ends in .h is a rule for a specific file.
1374 if added_dep.endswith('.h'):
1375 results.add(added_dep)
1376 else:
1377 results.add(os_path.join(added_dep, 'DEPS'))
1378 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231379
1380
1381def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Jeremy Leconte3d476f22023-10-17 13:04:241382 """When a dependency prefixed with + is added to a DEPS file, we
Edward Lesmesbef08502021-02-05 22:08:321383 want to make sure that the change is reviewed by an OWNER of the
1384 target file or directory, to avoid layering violations from being
1385 introduced. This check verifies that this happens.
1386 """
Jeremy Leconte3d476f22023-10-17 13:04:241387 virtual_depended_on_files = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231388
Jeremy Leconte3d476f22023-10-17 13:04:241389 file_filter = lambda f: not input_api.re.match(
1390 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1391 for f in input_api.AffectedFiles(include_deletes=False,
1392 file_filter=file_filter):
1393 filename = input_api.os_path.basename(f.LocalPath())
1394 if filename == 'DEPS':
1395 virtual_depended_on_files.update(
1396 _CalculateAddedDeps(input_api.os_path,
1397 '\n'.join(f.OldContents()),
1398 '\n'.join(f.NewContents())))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231399
Jeremy Leconte3d476f22023-10-17 13:04:241400 if not virtual_depended_on_files:
1401 return []
Christoffer Jansson4e8a7732022-02-08 08:01:121402
Jeremy Leconte3d476f22023-10-17 13:04:241403 if input_api.is_committing:
1404 if input_api.tbr:
1405 return [
1406 output_api.PresubmitNotifyResult(
1407 '--tbr was specified, skipping OWNERS check for DEPS '
1408 'additions')
1409 ]
1410 if input_api.dry_run:
1411 return [
1412 output_api.PresubmitNotifyResult(
1413 'This is a dry run, skipping OWNERS check for DEPS '
1414 'additions')
1415 ]
1416 if not input_api.change.issue:
1417 return [
1418 output_api.PresubmitError(
1419 "DEPS approval by OWNERS check failed: this change has "
1420 "no change number, so we can't check it for approvals.")
1421 ]
1422 output = output_api.PresubmitError
1423 else:
1424 output = output_api.PresubmitNotifyResult
Christoffer Jansson4e8a7732022-02-08 08:01:121425
Jeremy Leconte3d476f22023-10-17 13:04:241426 owner_email, reviewers = (
1427 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1428 input_api, None, approval_needed=input_api.is_committing))
Christoffer Jansson4e8a7732022-02-08 08:01:121429
Jeremy Leconte3d476f22023-10-17 13:04:241430 owner_email = owner_email or input_api.change.author_email
Christoffer Jansson4e8a7732022-02-08 08:01:121431
Jeremy Leconte3d476f22023-10-17 13:04:241432 approval_status = input_api.owners_client.GetFilesApprovalStatus(
1433 virtual_depended_on_files, reviewers.union([owner_email]), [])
1434 missing_files = [
1435 f for f in virtual_depended_on_files
1436 if approval_status[f] != input_api.owners_client.APPROVED
Christoffer Jansson4e8a7732022-02-08 08:01:121437 ]
Christoffer Jansson4e8a7732022-02-08 08:01:121438
Jeremy Leconte3d476f22023-10-17 13:04:241439 # We strip the /DEPS part that was added by
1440 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1441 # directory.
1442 def StripDeps(path):
1443 start_deps = path.rfind('/DEPS')
1444 if start_deps != -1:
1445 return path[:start_deps]
1446 return path
1447
1448 unapproved_dependencies = [
1449 "'+%s'," % StripDeps(path) for path in missing_files
1450 ]
1451
1452 if unapproved_dependencies:
1453 output_list = [
1454 output(
1455 'You need LGTM from owners of depends-on paths in DEPS that '
1456 ' were modified in this CL:\n %s' %
1457 '\n '.join(sorted(unapproved_dependencies)))
1458 ]
1459 suggested_owners = input_api.owners_client.SuggestOwners(
1460 missing_files, exclude=[owner_email])
1461 output_list.append(
1462 output('Suggested missing target path OWNERS:\n %s' %
1463 '\n '.join(suggested_owners or [])))
1464 return output_list
1465
1466 return []