blob: 4fbee0e32dc364ee7ebfc317616c17489f2f137b [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
jbauchc4e3ead2016-02-19 08:25:5550# These filters will always be removed, even if the caller specifies a filter
51# set, as they are problematic or broken in some way.
52#
53# Justifications for each filter:
54# - build/c++11 : Rvalue ref checks are unreliable (false positives),
Mirko Bonadeifc17a782020-06-30 12:31:3755# include file and feature blocklists are
jbauchc4e3ead2016-02-19 08:25:5556# google3-specific.
Mirko Bonadeie92e2862020-05-29 13:23:0957# - runtime/references : Mutable references are not banned by the Google
58# C++ style guide anymore (starting from May 2020).
kjellandere5a87a52016-04-27 09:32:1259# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
60# all move-related errors).
Mirko Bonadeifc17a782020-06-30 12:31:3761DISABLED_LINT_FILTERS = [
Mirko Bonadei8cc66952020-10-30 09:13:4562 '-build/c++11',
63 '-runtime/references',
64 '-whitespace/operators',
jbauchc4e3ead2016-02-19 08:25:5565]
66
kjellanderfd595232015-12-04 10:44:0967# List of directories of "supported" native APIs. That means changes to headers
68# will be done in a compatible way following this scheme:
69# 1. Non-breaking changes are made.
70# 2. The old APIs as marked as deprecated (with comments).
71# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
72# webrtc-users@google.com (internal list).
73# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-03 07:56:1474NATIVE_API_DIRS = (
Mirko Bonadei8cc66952020-10-30 09:13:4575 'api', # All subdirectories of api/ are included as well.
76 'media/base',
77 'media/engine',
78 'modules/audio_device/include',
79 'pc',
kjellanderdd705472016-06-09 18:17:2780)
Mirko Bonadei4dc4e252017-09-19 11:49:1681
kjellanderdd705472016-06-09 18:17:2782# These directories should not be used but are maintained only to avoid breaking
83# some legacy downstream code.
84LEGACY_API_DIRS = (
Mirko Bonadei8cc66952020-10-30 09:13:4585 'common_audio/include',
86 'modules/audio_coding/include',
87 'modules/audio_processing/include',
88 'modules/congestion_controller/include',
89 'modules/include',
90 'modules/remote_bitrate_estimator/include',
91 'modules/rtp_rtcp/include',
92 'modules/rtp_rtcp/source',
93 'modules/utility/include',
94 'modules/video_coding/codecs/h264/include',
95 'modules/video_coding/codecs/vp8/include',
96 'modules/video_coding/codecs/vp9/include',
97 'modules/video_coding/include',
98 'rtc_base',
99 'system_wrappers/include',
kjellander53047c92015-12-03 07:56:14100)
Mirko Bonadei4dc4e252017-09-19 11:49:16101
Karl Wibergd4f01c12017-11-10 09:55:45102# NOTE: The set of directories in API_DIRS should be the same as those
103# listed in the table in native-api.md.
kjellanderdd705472016-06-09 18:17:27104API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-03 07:56:14105
Mirko Bonadei4dc4e252017-09-19 11:49:16106# TARGET_RE matches a GN target, and extracts the target name and the contents.
Mirko Bonadei2dcf3482020-06-05 12:30:41107TARGET_RE = re.compile(
Mirko Bonadei8cc66952020-10-30 09:13:45108 r'(?P<indent>\s*)(?P<target_type>\w+)\("(?P<target_name>\w+)"\) {'
109 r'(?P<target_contents>.*?)'
110 r'(?P=indent)}', re.MULTILINE | re.DOTALL)
Mirko Bonadei4dc4e252017-09-19 11:49:16111
112# SOURCES_RE matches a block of sources inside a GN target.
113SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
114 re.MULTILINE | re.DOTALL)
115
Mirko Bonadei2dcf3482020-06-05 12:30:41116# DEPS_RE matches a block of sources inside a GN target.
117DEPS_RE = re.compile(r'\bdeps \+?= \[(?P<deps>.*?)\]',
118 re.MULTILINE | re.DOTALL)
119
Philipp Hancke0c2a9ca2021-08-11 10:00:27120# FILE_PATH_RE matches a file path.
Mirko Bonadei4dc4e252017-09-19 11:49:16121FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
122
kjellander53047c92015-12-03 07:56:14123
Mirko Bonadeid8665442018-09-04 10:17:27124def FindSrcDirPath(starting_dir):
Christoffer Jansson4e8a7732022-02-08 08:01:12125 """Returns the abs path to the src/ dir of the project."""
126 src_dir = starting_dir
127 while os.path.basename(src_dir) != 'src':
128 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
129 return src_dir
Mirko Bonadeid8665442018-09-04 10:17:27130
131
Oleh Prypin2f33a562017-10-04 18:17:54132@contextmanager
133def _AddToPath(*paths):
Christoffer Jansson4e8a7732022-02-08 08:01:12134 original_sys_path = sys.path
135 sys.path.extend(paths)
136 try:
137 yield
138 finally:
139 # Restore sys.path to what it was before.
140 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 13:27:22141
142
charujain9893e252017-09-14 11:33:22143def VerifyNativeApiHeadersListIsValid(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12144 """Ensures the list of native API header directories is up to date."""
145 non_existing_paths = []
146 native_api_full_paths = [
147 input_api.os_path.join(input_api.PresubmitLocalPath(), *path.split('/'))
148 for path in API_DIRS
149 ]
150 for path in native_api_full_paths:
151 if not os.path.isdir(path):
152 non_existing_paths.append(path)
153 if non_existing_paths:
154 return [
155 output_api.PresubmitError(
156 'Directories to native API headers have changed which has made '
157 'the list in PRESUBMIT.py outdated.\nPlease update it to the '
158 'current location of our native APIs.', non_existing_paths)
Mirko Bonadei8cc66952020-10-30 09:13:45159 ]
Christoffer Jansson4e8a7732022-02-08 08:01:12160 return []
kjellander53047c92015-12-03 07:56:14161
Artem Titove92675b2018-05-22 08:21:27162
kjellanderc88b5d52017-04-05 13:42:43163API_CHANGE_MSG = """
kwibergeb133022016-04-07 14:41:48164You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 12:13:23165 1. Make compatible changes that don't break existing clients. Usually
166 this is done by keeping the existing method signatures unchanged.
Danil Chapovalov7013b3b2021-02-22 13:31:26167 2. Mark the old stuff as deprecated (use the ABSL_DEPRECATED macro).
kwibergeb133022016-04-07 14:41:48168 3. Create a timeline and plan for when the deprecated stuff will be
169 removed. (The amount of time we give users to change their code
170 should be informed by how much work it is for them. If they just
171 need to replace one name with another or something equally
172 simple, 1-2 weeks might be good; if they need to do serious work,
173 up to 3 months may be called for.)
174 4. Update/inform existing downstream code owners to stop using the
175 deprecated stuff. (Send announcements to
176 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
177 5. Remove the deprecated stuff, once the agreed-upon amount of time
178 has passed.
179Related files:
180"""
kjellander53047c92015-12-03 07:56:14181
Artem Titove92675b2018-05-22 08:21:27182
charujain9893e252017-09-14 11:33:22183def CheckNativeApiHeaderChanges(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12184 """Checks to remind proper changing of native APIs."""
185 files = []
186 source_file_filter = lambda x: input_api.FilterSourceFile(
187 x, files_to_check=[r'.+\.(gn|gni|h)$'])
188 for f in input_api.AffectedSourceFiles(source_file_filter):
189 for path in API_DIRS:
190 dn = os.path.dirname(f.LocalPath())
191 if path == 'api':
192 # Special case: Subdirectories included.
193 if dn == 'api' or dn.startswith('api/'):
194 files.append(f.LocalPath())
195 else:
196 # Normal case: Subdirectories not included.
197 if dn == path:
198 files.append(f.LocalPath())
kjellander53047c92015-12-03 07:56:14199
Christoffer Jansson4e8a7732022-02-08 08:01:12200 if files:
201 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
202 return []
kjellander53047c92015-12-03 07:56:14203
kjellander@webrtc.org0fcaf992015-11-26 14:24:52204
Mirko Bonadei8cc66952020-10-30 09:13:45205def CheckNoIOStreamInHeaders(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:12206 """Checks to make sure no .h files include <iostream>."""
207 files = []
208 pattern = input_api.re.compile(r'^#include\s*<iostream>',
209 input_api.re.MULTILINE)
210 file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter(
211 x))
212 for f in input_api.AffectedSourceFiles(file_filter):
213 if not f.LocalPath().endswith('.h'):
214 continue
215 contents = input_api.ReadFile(f)
216 if pattern.search(contents):
217 files.append(f)
kjellander@webrtc.org51198f12012-02-21 17:53:46218
Christoffer Jansson4e8a7732022-02-08 08:01:12219 if len(files) > 0:
220 return [
221 output_api.PresubmitError(
222 'Do not #include <iostream> in header files, since it inserts '
223 'static initialization into every file including the header. '
224 'Instead, #include <ostream>. See http://crbug.com/94794', files)
225 ]
226 return []
kjellander@webrtc.org51198f12012-02-21 17:53:46227
kjellander@webrtc.orge4158642014-08-06 09:11:18228
Mirko Bonadei8cc66952020-10-30 09:13:45229def CheckNoPragmaOnce(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:12230 """Make sure that banned functions are not used."""
231 files = []
232 pattern = input_api.re.compile(r'^#pragma\s+once', input_api.re.MULTILINE)
233 file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter(
234 x))
235 for f in input_api.AffectedSourceFiles(file_filter):
236 if not f.LocalPath().endswith('.h'):
237 continue
238 contents = input_api.ReadFile(f)
239 if pattern.search(contents):
240 files.append(f)
kjellander6aeef742017-02-20 09:13:18241
Christoffer Jansson4e8a7732022-02-08 08:01:12242 if files:
243 return [
244 output_api.PresubmitError(
245 'Do not use #pragma once in header files.\n'
246 'See http://www.chromium.org/developers/coding-style'
247 '#TOC-File-headers', files)
248 ]
249 return []
250
kjellander6aeef742017-02-20 09:13:18251
Byoungchan Lee94f2ef22021-07-01 13:21:44252def CheckNoFRIEND_TEST(# pylint: disable=invalid-name
Mirko Bonadei8cc66952020-10-30 09:13:45253 input_api,
Byoungchan Lee94f2ef22021-07-01 13:21:44254 output_api,
Mirko Bonadei8cc66952020-10-30 09:13:45255 source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:12256 """Make sure that gtest's FRIEND_TEST() macro is not used, the
kjellander@webrtc.org51198f12012-02-21 17:53:46257 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
258 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
Christoffer Jansson4e8a7732022-02-08 08:01:12259 problems = []
kjellander@webrtc.org51198f12012-02-21 17:53:46260
Christoffer Jansson4e8a7732022-02-08 08:01:12261 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
262 source_file_filter(f))
263 for f in input_api.AffectedFiles(file_filter=file_filter):
264 for line_num, line in f.ChangedContents():
265 if 'FRIEND_TEST(' in line:
266 problems.append(' %s:%d' % (f.LocalPath(), line_num))
kjellander@webrtc.org51198f12012-02-21 17:53:46267
Christoffer Jansson4e8a7732022-02-08 08:01:12268 if not problems:
269 return []
270 return [
271 output_api.PresubmitPromptWarning(
272 'WebRTC\'s code should not use gtest\'s FRIEND_TEST() macro. '
273 'Include testsupport/gtest_prod_util.h and use '
274 'FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))
275 ]
kjellander@webrtc.org51198f12012-02-21 17:53:46276
kjellander@webrtc.orge4158642014-08-06 09:11:18277
Mirko Bonadeifc17a782020-06-30 12:31:37278def IsLintDisabled(disabled_paths, file_path):
Christoffer Jansson4e8a7732022-02-08 08:01:12279 """ Checks if a file is disabled for lint check."""
280 for path in disabled_paths:
281 if file_path == path or os.path.dirname(file_path).startswith(path):
282 return True
283 return False
kjellander@webrtc.org0fcaf992015-11-26 14:24:52284
285
charujain9893e252017-09-14 11:33:22286def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 09:23:00287 source_file_filter=None):
Christoffer Jansson4e8a7732022-02-08 08:01:12288 """Checks that all new or non-exempt .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 11:33:22289 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46290 depot_tools/presubmit_canned_checks.py but has less filters and only checks
291 added files."""
Christoffer Jansson4e8a7732022-02-08 08:01:12292 result = []
kjellander@webrtc.org51198f12012-02-21 17:53:46293
Christoffer Jansson4e8a7732022-02-08 08:01:12294 # Initialize cpplint.
295 import cpplint
296 # Access to a protected member _XX of a client class
297 # pylint: disable=W0212
298 cpplint._cpplint_state.ResetErrorCounts()
kjellander@webrtc.org51198f12012-02-21 17:53:46299
Christoffer Jansson4e8a7732022-02-08 08:01:12300 lint_filters = cpplint._Filters()
301 lint_filters.extend(DISABLED_LINT_FILTERS)
302 cpplint._SetFilters(','.join(lint_filters))
jbauchc4e3ead2016-02-19 08:25:55303
Christoffer Jansson4e8a7732022-02-08 08:01:12304 # Create a platform independent exempt list for cpplint.
305 disabled_paths = [
306 input_api.os_path.join(*path.split('/')) for path in CPPLINT_EXCEPTIONS
307 ]
kjellander@webrtc.org0fcaf992015-11-26 14:24:52308
Christoffer Jansson4e8a7732022-02-08 08:01:12309 # Use the strictest verbosity level for cpplint.py (level 1) which is the
310 # default when running cpplint.py from command line. To make it possible to
311 # work with not-yet-converted code, we're only applying it to new (or
312 # moved/renamed) files and files not listed in CPPLINT_EXCEPTIONS.
313 verbosity_level = 1
314 files = []
315 for f in input_api.AffectedSourceFiles(source_file_filter):
316 # Note that moved/renamed files also count as added.
317 if f.Action() == 'A' or not IsLintDisabled(disabled_paths, f.LocalPath()):
318 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23319
Christoffer Jansson4e8a7732022-02-08 08:01:12320 for file_name in files:
321 cpplint.ProcessFile(file_name, verbosity_level)
kjellander@webrtc.org51198f12012-02-21 17:53:46322
Christoffer Jansson4e8a7732022-02-08 08:01:12323 if cpplint._cpplint_state.error_count > 0:
324 if input_api.is_committing:
325 res_type = output_api.PresubmitError
326 else:
327 res_type = output_api.PresubmitPromptWarning
328 result = [res_type('Changelist failed cpplint.py check.')]
kjellander@webrtc.org51198f12012-02-21 17:53:46329
Christoffer Jansson4e8a7732022-02-08 08:01:12330 return result
kjellander@webrtc.org51198f12012-02-21 17:53:46331
Artem Titove92675b2018-05-22 08:21:27332
charujain9893e252017-09-14 11:33:22333def CheckNoSourcesAbove(input_api, gn_files, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12334 # Disallow referencing source files with paths above the GN file location.
335 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
336 re.MULTILINE | re.DOTALL)
337 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
338 violating_gn_files = set()
339 violating_source_entries = []
340 for gn_file in gn_files:
341 contents = input_api.ReadFile(gn_file)
342 for source_block_match in source_pattern.finditer(contents):
343 # Find all source list entries starting with ../ in the source block
344 # (exclude overrides entries).
345 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
346 source_file = file_list_match.group(1)
347 if 'overrides/' not in source_file:
348 violating_source_entries.append(source_file)
349 violating_gn_files.add(gn_file)
350 if violating_gn_files:
351 return [
352 output_api.PresubmitError(
353 'Referencing source files above the directory of the GN file '
354 'is not allowed. Please introduce new GN targets in the proper '
355 'location instead.\n'
356 'Invalid source entries:\n'
357 '%s\n'
358 'Violating GN files:' % '\n'.join(violating_source_entries),
359 items=violating_gn_files)
360 ]
361 return []
ehmaldonado5b1ba082016-09-02 12:51:08362
Artem Titove92675b2018-05-22 08:21:27363
Mirko Bonadei2dcf3482020-06-05 12:30:41364def CheckAbseilDependencies(input_api, gn_files, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12365 """Checks that Abseil dependencies are declared in `absl_deps`."""
366 absl_re = re.compile(r'third_party/abseil-cpp', re.MULTILINE | re.DOTALL)
367 target_types_to_check = [
368 'rtc_library',
369 'rtc_source_set',
370 'rtc_static_library',
371 'webrtc_fuzzer_test',
372 ]
373 error_msg = ('Abseil dependencies in target "%s" (file: %s) '
374 'should be moved to the "absl_deps" parameter.')
375 errors = []
Mirko Bonadei2dcf3482020-06-05 12:30:41376
Christoffer Jansson4e8a7732022-02-08 08:01:12377 # pylint: disable=too-many-nested-blocks
378 for gn_file in gn_files:
379 gn_file_content = input_api.ReadFile(gn_file)
380 for target_match in TARGET_RE.finditer(gn_file_content):
381 target_type = target_match.group('target_type')
382 target_name = target_match.group('target_name')
383 target_contents = target_match.group('target_contents')
384 if target_type in target_types_to_check:
385 for deps_match in DEPS_RE.finditer(target_contents):
386 deps = deps_match.group('deps').splitlines()
387 for dep in deps:
388 if re.search(absl_re, dep):
389 errors.append(
390 output_api.PresubmitError(error_msg %
391 (target_name, gn_file.LocalPath())))
392 break # no need to warn more than once per target
393 return errors
Mirko Bonadei2dcf3482020-06-05 12:30:41394
395
Mirko Bonadei4dc4e252017-09-19 11:49:16396def CheckNoMixingSources(input_api, gn_files, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12397 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
Mirko Bonadei4dc4e252017-09-19 11:49:16398
399 See bugs.webrtc.org/7743 for more context.
400 """
Artem Titove92675b2018-05-22 08:21:27401
Christoffer Jansson4e8a7732022-02-08 08:01:12402 def _MoreThanOneSourceUsed(*sources_lists):
403 sources_used = 0
404 for source_list in sources_lists:
405 if len(source_list) > 0:
406 sources_used += 1
407 return sources_used > 1
Mirko Bonadei4dc4e252017-09-19 11:49:16408
Christoffer Jansson4e8a7732022-02-08 08:01:12409 errors = defaultdict(lambda: [])
410 for gn_file in gn_files:
411 gn_file_content = input_api.ReadFile(gn_file)
412 for target_match in TARGET_RE.finditer(gn_file_content):
413 # list_of_sources is a list of tuples of the form
414 # (c_files, cc_files, objc_files) that keeps track of all the
415 # sources defined in a target. A GN target can have more that
416 # on definition of sources (since it supports if/else statements).
417 # E.g.:
418 # rtc_static_library("foo") {
419 # if (is_win) {
420 # sources = [ "foo.cc" ]
421 # } else {
422 # sources = [ "foo.mm" ]
423 # }
424 # }
425 # This is allowed and the presubmit check should support this case.
426 list_of_sources = []
427 c_files = []
428 cc_files = []
429 objc_files = []
430 target_name = target_match.group('target_name')
431 target_contents = target_match.group('target_contents')
432 for sources_match in SOURCES_RE.finditer(target_contents):
433 if '+=' not in sources_match.group(0):
434 if c_files or cc_files or objc_files:
Mirko Bonadei4dc4e252017-09-19 11:49:16435 list_of_sources.append((c_files, cc_files, objc_files))
Christoffer Jansson4e8a7732022-02-08 08:01:12436 c_files = []
437 cc_files = []
438 objc_files = []
439 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
440 file_path = file_match.group('file_path')
441 extension = file_match.group('extension')
442 if extension == '.c':
443 c_files.append(file_path + extension)
444 if extension == '.cc':
445 cc_files.append(file_path + extension)
446 if extension in ['.m', '.mm']:
447 objc_files.append(file_path + extension)
448 list_of_sources.append((c_files, cc_files, objc_files))
449 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
450 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
451 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
452 errors[gn_file.LocalPath()].append((target_name, all_sources))
453 if errors:
454 return [
455 output_api.PresubmitError(
456 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
457 'Please create a separate target for each collection of '
458 'sources.\n'
459 'Mixed sources: \n'
460 '%s\n'
461 'Violating GN files:\n%s\n' %
462 (json.dumps(errors, indent=2), '\n'.join(list(errors.keys()))))
463 ]
464 return []
kjellander7439f972016-12-06 06:47:46465
Artem Titove92675b2018-05-22 08:21:27466
charujain9893e252017-09-14 11:33:22467def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12468 cwd = input_api.PresubmitLocalPath()
469 with _AddToPath(
470 input_api.os_path.join(cwd, 'tools_webrtc', 'presubmit_checks_lib')):
471 from check_package_boundaries import CheckPackageBoundaries
472 build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
473 errors = CheckPackageBoundaries(cwd, build_files)[:5]
474 if errors:
475 return [
476 output_api.PresubmitError(
477 'There are package boundary violations in the following GN '
478 'files:',
479 long_text='\n\n'.join(str(err) for err in errors))
Mirko Bonadei8cc66952020-10-30 09:13:45480 ]
Christoffer Jansson4e8a7732022-02-08 08:01:12481 return []
ehmaldonado4fb97462017-01-30 13:27:22482
Mirko Bonadeia51bbd82018-03-08 15:15:45483
Mirko Bonadeif0e0d752018-07-04 06:48:18484def _ReportFileAndLine(filename, line_num):
Christoffer Jansson4e8a7732022-02-08 08:01:12485 """Default error formatter for _FindNewViolationsOfRule."""
486 return '%s (line %s)' % (filename, line_num)
Mirko Bonadeia51bbd82018-03-08 15:15:45487
488
Mirko Bonadei8cc66952020-10-30 09:13:45489def CheckNoWarningSuppressionFlagsAreAdded(gn_files,
490 input_api,
491 output_api,
Mirko Bonadeif0e0d752018-07-04 06:48:18492 error_formatter=_ReportFileAndLine):
Christoffer Jansson4e8a7732022-02-08 08:01:12493 """Ensure warning suppression flags are not added without a reason."""
494 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
495 'in WebRTC.\n'
496 'If you are not adding this code (e.g. you are just moving '
497 'existing code) or you want to add an exception,\n'
498 'you can add a comment on the line that causes the problem:\n\n'
499 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
500 '\n'
501 'Affected files:\n')
502 errors = [] # 2-element tuples with (file, line number)
503 clang_warn_re = input_api.re.compile(r'//build/config/clang:extra_warnings')
504 # pylint: disable-next=fixme
505 no_presubmit_re = input_api.re.compile(
506 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
507 for f in gn_files:
508 for line_num, line in f.ChangedContents():
509 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
510 errors.append(error_formatter(f.LocalPath(), line_num))
511 if errors:
512 return [output_api.PresubmitError(msg, errors)]
513 return []
Mirko Bonadeif0e0d752018-07-04 06:48:18514
Mirko Bonadei9ce800d2019-02-05 15:48:13515
Mirko Bonadei8cc66952020-10-30 09:13:45516def CheckNoTestCaseUsageIsAdded(input_api,
517 output_api,
518 source_file_filter,
Mirko Bonadei9ce800d2019-02-05 15:48:13519 error_formatter=_ReportFileAndLine):
Christoffer Jansson4e8a7732022-02-08 08:01:12520 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
521 'new API: https://github.com/google/googletest/blob/master/'
522 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
523 'Affected files:\n')
524 errors = [] # 2-element tuples with (file, line number)
525 test_case_re = input_api.re.compile(r'TEST_CASE')
526 file_filter = lambda f: (source_file_filter(f) and f.LocalPath().endswith(
527 '.cc'))
528 for f in input_api.AffectedSourceFiles(file_filter):
529 for line_num, line in f.ChangedContents():
530 if test_case_re.search(line):
531 errors.append(error_formatter(f.LocalPath(), line_num))
532 if errors:
533 return [output_api.PresubmitError(error_msg, errors)]
534 return []
Mirko Bonadei9ce800d2019-02-05 15:48:13535
536
Mirko Bonadei8cc66952020-10-30 09:13:45537def CheckNoStreamUsageIsAdded(input_api,
538 output_api,
Artem Titov739351d2018-05-11 10:21:36539 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 06:48:18540 error_formatter=_ReportFileAndLine):
Christoffer Jansson4e8a7732022-02-08 08:01:12541 """Make sure that no more dependencies on stringstream are added."""
542 error_msg = ('Usage of <sstream>, <istream> and <ostream> in WebRTC is '
543 'deprecated.\n'
544 'This includes the following types:\n'
545 'std::istringstream, std::ostringstream, std::wistringstream, '
546 'std::wostringstream,\n'
547 'std::wstringstream, std::ostream, std::wostream, std::istream,'
548 'std::wistream,\n'
549 'std::iostream, std::wiostream.\n'
550 'If you are not adding this code (e.g. you are just moving '
551 'existing code),\n'
552 'you can add a comment on the line that causes the problem:\n\n'
553 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
554 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
555 '\n'
556 'If you are adding new code, consider using '
557 'rtc::SimpleStringBuilder\n'
558 '(in rtc_base/strings/string_builder.h).\n'
559 'Affected files:\n')
560 errors = [] # 2-element tuples with (file, line number)
561 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
562 usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
563 no_presubmit_re = input_api.re.compile(
564 r'// no-presubmit-check TODO\(webrtc:8982\)')
565 file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter(
566 x))
Mirko Bonadei571791a2019-05-07 12:08:05567
Christoffer Jansson4e8a7732022-02-08 08:01:12568 def _IsException(file_path):
569 is_test = any(
570 file_path.endswith(x)
571 for x in ['_test.cc', '_tests.cc', '_unittest.cc', '_unittests.cc'])
572 return (file_path.startswith('examples') or file_path.startswith('test')
573 or is_test)
Patrik Höglund2ea27962020-01-13 14:10:40574
Christoffer Jansson4e8a7732022-02-08 08:01:12575 for f in input_api.AffectedSourceFiles(file_filter):
576 # Usage of stringstream is allowed under examples/ and in tests.
577 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
578 continue
579 for line_num, line in f.ChangedContents():
580 if ((include_re.search(line) or usage_re.search(line))
581 and not no_presubmit_re.search(line)):
582 errors.append(error_formatter(f.LocalPath(), line_num))
583 if errors:
584 return [output_api.PresubmitError(error_msg, errors)]
585 return []
Mirko Bonadeia51bbd82018-03-08 15:15:45586
Artem Titove92675b2018-05-22 08:21:27587
Mirko Bonadeia05d47e2018-05-09 09:03:38588def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12589 """Checks that public_deps is not used without a good reason."""
590 result = []
591 no_presubmit_check_re = input_api.re.compile(
592 r'# no-presubmit-check TODO\(webrtc:\d+\)')
593 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
594 'because it doesn\'t map well to downstream build systems.\n'
595 'Used in: %s (line %d).\n'
596 'If you are not adding this code (e.g. you are just moving '
597 'existing code) or you have a good reason, you can add this '
598 'comment (verbatim) on the line that causes the problem:\n\n'
599 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
600 for affected_file in gn_files:
601 for (line_number, affected_line) in affected_file.ChangedContents():
602 if 'public_deps' in affected_line:
603 surpressed = no_presubmit_check_re.search(affected_line)
604 if not surpressed:
605 result.append(
606 output_api.PresubmitError(
607 error_msg % (affected_file.LocalPath(), line_number)))
608 return result
Mirko Bonadei5c1ad592017-12-12 10:52:27609
Artem Titove92675b2018-05-22 08:21:27610
Mirko Bonadei05691dd2019-10-22 14:34:24611def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12612 result = []
613 error_msg = ('check_includes overrides are not allowed since it can cause '
614 'incorrect dependencies to form. It effectively means that your '
615 'module can include any .h file without depending on its '
616 'corresponding target. There are some exceptional cases when '
617 'this is allowed: if so, get approval from a .gn owner in the '
618 'root OWNERS file.\n'
619 'Used in: %s (line %d).')
620 # pylint: disable-next=fixme
621 no_presubmit_re = input_api.re.compile(
622 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
623 for affected_file in gn_files:
624 for (line_number, affected_line) in affected_file.ChangedContents():
625 if ('check_includes' in affected_line
626 and not no_presubmit_re.search(affected_line)):
627 result.append(
628 output_api.PresubmitError(error_msg %
629 (affected_file.LocalPath(), line_number)))
630 return result
Patrik Höglund6f491062018-01-11 11:04:23631
Artem Titove92675b2018-05-22 08:21:27632
Mirko Bonadeif0e0d752018-07-04 06:48:18633def CheckGnChanges(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12634 file_filter = lambda x: (input_api.FilterSourceFile(
635 x,
636 files_to_check=(r'.+\.(gn|gni)$', ),
637 files_to_skip=(r'.*/presubmit_checks_lib/testdata/.*', )))
ehmaldonado5b1ba082016-09-02 12:51:08638
Christoffer Jansson4e8a7732022-02-08 08:01:12639 gn_files = []
640 for f in input_api.AffectedSourceFiles(file_filter):
641 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 12:51:08642
Christoffer Jansson4e8a7732022-02-08 08:01:12643 result = []
644 if gn_files:
645 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
646 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
647 result.extend(CheckAbseilDependencies(input_api, gn_files, output_api))
648 result.extend(
649 CheckNoPackageBoundaryViolations(input_api, gn_files, output_api))
650 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
651 result.extend(CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
652 result.extend(
653 CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api))
654 return result
ehmaldonado5b1ba082016-09-02 12:51:08655
Artem Titove92675b2018-05-22 08:21:27656
Oleh Prypin920b6532017-10-05 09:28:51657def CheckGnGen(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12658 """Runs `gn gen --check` with default args to detect mismatches between
Oleh Prypin920b6532017-10-05 09:28:51659 #includes and dependencies in the BUILD.gn files, as well as general build
660 errors.
661 """
Christoffer Jansson4e8a7732022-02-08 08:01:12662 with _AddToPath(
663 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools_webrtc',
664 'presubmit_checks_lib')):
665 from build_helpers import RunGnCheck
666 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
667 if errors:
668 return [
669 output_api.PresubmitPromptWarning(
670 'Some #includes do not match the build dependency graph. '
671 'Please run:\n'
672 ' gn gen --check <out_dir>',
673 long_text='\n\n'.join(errors))
674 ]
675 return []
Oleh Prypin920b6532017-10-05 09:28:51676
Artem Titove92675b2018-05-22 08:21:27677
Artem Titova04d1402018-05-11 09:23:00678def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:12679 """Runs checkdeps on #include statements added in this
kjellander@webrtc.org3bd41562014-09-01 11:06:37680 change. Breaking - rules is an error, breaking ! rules is a
681 warning.
682 """
Christoffer Jansson4e8a7732022-02-08 08:01:12683 # Copied from Chromium's src/PRESUBMIT.py.
kjellander@webrtc.org3bd41562014-09-01 11:06:37684
Christoffer Jansson4e8a7732022-02-08 08:01:12685 # We need to wait until we have an input_api object and use this
686 # roundabout construct to import checkdeps because this file is
687 # eval-ed and thus doesn't have __file__.
688 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
689 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
690 if not os.path.exists(checkdeps_path):
691 return [
692 output_api.PresubmitError(
693 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
694 'download all the DEPS entries?' % checkdeps_path)
695 ]
696 with _AddToPath(checkdeps_path):
697 import checkdeps
698 from cpp_checker import CppChecker
699 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37700
Christoffer Jansson4e8a7732022-02-08 08:01:12701 added_includes = []
702 for f in input_api.AffectedFiles(file_filter=source_file_filter):
703 if not CppChecker.IsCppFile(f.LocalPath()):
704 continue
kjellander@webrtc.org3bd41562014-09-01 11:06:37705
Christoffer Jansson4e8a7732022-02-08 08:01:12706 changed_lines = [line for _, line in f.ChangedContents()]
707 added_includes.append([f.LocalPath(), changed_lines])
kjellander@webrtc.org3bd41562014-09-01 11:06:37708
Christoffer Jansson4e8a7732022-02-08 08:01:12709 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
kjellander@webrtc.org3bd41562014-09-01 11:06:37710
Christoffer Jansson4e8a7732022-02-08 08:01:12711 error_descriptions = []
712 warning_descriptions = []
713 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
714 added_includes):
715 description_with_path = '%s\n %s' % (path, rule_description)
716 if rule_type == Rule.DISALLOW:
717 error_descriptions.append(description_with_path)
718 else:
719 warning_descriptions.append(description_with_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37720
Christoffer Jansson4e8a7732022-02-08 08:01:12721 results = []
722 if error_descriptions:
723 results.append(
724 output_api.PresubmitError(
725 'You added one or more #includes that violate checkdeps rules.'
726 '\nCheck that the DEPS files in these locations contain valid '
727 'rules.\nSee '
728 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
729 'for more details about checkdeps.', error_descriptions))
730 if warning_descriptions:
731 results.append(
732 output_api.PresubmitPromptOrNotify(
733 'You added one or more #includes of files that are temporarily'
734 '\nallowed but being removed. Can you avoid introducing the\n'
735 '#include? See relevant DEPS file(s) for details and contacts.'
736 '\nSee '
737 'https://cs.chromium.org/chromium/src/buildtools/checkdeps/ '
738 'for more details about checkdeps.', warning_descriptions))
739 return results
kjellander@webrtc.org3bd41562014-09-01 11:06:37740
Artem Titove92675b2018-05-22 08:21:27741
charujain9893e252017-09-14 11:33:22742def CheckCommitMessageBugEntry(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12743 """Check that bug entries are well-formed in commit message."""
744 bogus_bug_msg = (
745 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
746 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
747 results = []
748 for bug in input_api.change.BugsFromDescription():
749 bug = bug.strip()
750 if bug.lower() == 'none':
751 continue
752 if 'b/' not in bug and ':' not in bug:
753 try:
754 if int(bug) > 100000:
755 # Rough indicator for current chromium bugs.
756 prefix_guess = 'chromium'
757 else:
758 prefix_guess = 'webrtc'
759 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
760 (prefix_guess, bug))
761 except ValueError:
762 results.append(bogus_bug_msg % bug)
763 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
764 results.append(bogus_bug_msg % bug)
765 return [output_api.PresubmitError(r) for r in results]
charujain9893e252017-09-14 11:33:22766
Artem Titove92675b2018-05-22 08:21:27767
charujain9893e252017-09-14 11:33:22768def CheckChangeHasBugField(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12769 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 15:11:16770
771 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 13:12:35772 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 15:11:16773 a bug reference.
Mirko Bonadei61880182017-10-12 13:12:35774
775 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
776 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 15:11:16777 """
Christoffer Jansson4e8a7732022-02-08 08:01:12778 if input_api.change.BugsFromDescription():
779 return []
780 return [
781 output_api.PresubmitError(
782 'The "Bug: [bug number]" footer is mandatory. Please create a '
783 'bug and reference it using either of:\n'
784 ' * https://bugs.webrtc.org - reference it using Bug: '
785 'webrtc:XXXX\n'
786 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')
787 ]
kjellander@webrtc.orge4158642014-08-06 09:11:18788
Artem Titove92675b2018-05-22 08:21:27789
Artem Titova04d1402018-05-11 09:23:00790def CheckJSONParseErrors(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:12791 """Check that JSON files do not contain syntax errors."""
kjellander569cf942016-02-11 13:02:59792
Christoffer Jansson4e8a7732022-02-08 08:01:12793 def FilterFile(affected_file):
794 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
795 and source_file_filter(affected_file))
kjellander569cf942016-02-11 13:02:59796
Christoffer Jansson4e8a7732022-02-08 08:01:12797 def GetJSONParseError(input_api, filename):
798 try:
799 contents = input_api.ReadFile(filename)
800 input_api.json.loads(contents)
801 except ValueError as e:
802 return e
803 return None
kjellander569cf942016-02-11 13:02:59804
Christoffer Jansson4e8a7732022-02-08 08:01:12805 results = []
806 for affected_file in input_api.AffectedFiles(file_filter=FilterFile,
807 include_deletes=False):
808 parse_error = GetJSONParseError(input_api,
809 affected_file.AbsoluteLocalPath())
810 if parse_error:
811 results.append(
812 output_api.PresubmitError('%s could not be parsed: %s' %
813 (affected_file.LocalPath(), parse_error)))
814 return results
kjellander569cf942016-02-11 13:02:59815
816
charujain9893e252017-09-14 11:33:22817def RunPythonTests(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12818 def Join(*args):
819 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
Henrik Kjellander8d3ad822015-05-26 17:52:05820
Christoffer Jansson70098a82022-02-21 18:43:36821 excluded_files = [
Jeremy Leconte4fc9bd92022-03-18 09:21:07822 # These tests should be run manually after webrtc_dashboard_upload target
Christoffer Jansson70098a82022-02-21 18:43:36823 # has been built.
Jeremy Leconte4fc9bd92022-03-18 09:21:07824 'catapult_uploader_test.py',
825 'process_perf_results_test.py',
Christoffer Jansson70098a82022-02-21 18:43:36826 ]
827
Christoffer Jansson4e8a7732022-02-08 08:01:12828 test_directories = [
829 input_api.PresubmitLocalPath(),
830 Join('rtc_tools', 'py_event_log_analyzer'),
Christoffer Jansson4e8a7732022-02-08 08:01:12831 ] + [
832 root for root, _, files in os.walk(Join('tools_webrtc')) if any(
Christoffer Jansson70098a82022-02-21 18:43:36833 f.endswith('_test.py') and f not in excluded_files for f in files)
Christoffer Jansson4e8a7732022-02-08 08:01:12834 ]
Henrik Kjellander8d3ad822015-05-26 17:52:05835
Christoffer Jansson4e8a7732022-02-08 08:01:12836 tests = []
Christoffer Jansson1b083a92022-02-15 13:52:31837
Christoffer Jansson4e8a7732022-02-08 08:01:12838 for directory in test_directories:
839 tests.extend(
840 input_api.canned_checks.GetUnitTestsInDirectory(
841 input_api,
842 output_api,
843 directory,
844 files_to_check=[r'.+_test\.py$'],
845 run_on_python2=False))
846 return input_api.RunTests(tests, parallel=True)
Henrik Kjellander8d3ad822015-05-26 17:52:05847
848
Artem Titova04d1402018-05-11 09:23:00849def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
850 source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:12851 """Checks that the namespace google::protobuf has not been used."""
852 files = []
853 pattern = input_api.re.compile(r'google::protobuf')
854 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
855 file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter(
856 x))
857 for f in input_api.AffectedSourceFiles(file_filter):
858 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
859 continue
860 contents = input_api.ReadFile(f)
861 if pattern.search(contents):
862 files.append(f)
mbonadei38415b22017-04-07 12:38:01863
Christoffer Jansson4e8a7732022-02-08 08:01:12864 if files:
865 return [
866 output_api.PresubmitError(
867 'Please avoid to use namespace `google::protobuf` directly.\n'
868 'Add a using directive in `%s` and include that header instead.' %
869 proto_utils_path, files)
870 ]
871 return []
mbonadei38415b22017-04-07 12:38:01872
873
Mirko Bonadei92ea95e2017-09-15 04:47:31874def _LicenseHeader(input_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12875 """Returns the license header regexp."""
876 # Accept any year number from 2003 to the current year
877 current_year = int(input_api.time.strftime('%Y'))
878 allowed_years = (str(s) for s in reversed(range(2003, current_year + 1)))
879 years_re = '(' + '|'.join(allowed_years) + ')'
880 license_header = (
881 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
882 r'All [Rr]ights [Rr]eserved\.\n'
883 r'.*?\n'
884 r'.*? Use of this source code is governed by a BSD-style license\n'
885 r'.*? that can be found in the LICENSE file in the root of the source\n'
886 r'.*? tree\. An additional intellectual property rights grant can be '
887 r'found\n'
888 r'.*? in the file PATENTS\. All contributing project authors may\n'
889 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
890 ) % {
891 'year': years_re,
892 }
893 return license_header
Mirko Bonadei92ea95e2017-09-15 04:47:31894
895
charujain9893e252017-09-14 11:33:22896def CommonChecks(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:12897 """Checks common to both upload and commit."""
898 results = []
899 # Filter out files that are in objc or ios dirs from being cpplint-ed since
900 # they do not follow C++ lint rules.
901 exception_list = input_api.DEFAULT_FILES_TO_SKIP + (
902 r".*\bobjc[\\\/].*",
903 r".*objc\.[hcm]+$",
904 )
905 source_file_filter = lambda x: input_api.FilterSourceFile(
906 x, None, exception_list)
907 results.extend(
908 CheckApprovedFilesLintClean(input_api, output_api, source_file_filter))
909 results.extend(
910 input_api.canned_checks.CheckLicense(input_api, output_api,
911 _LicenseHeader(input_api)))
Byoungchan Lee94f2ef22021-07-01 13:21:44912
Christoffer Jansson4e8a7732022-02-08 08:01:12913 # TODO(bugs.webrtc.org/12114): Delete this filter and run pylint on
914 # all python files. This is a temporary solution.
915 python_file_filter = lambda f: (f.LocalPath().endswith('.py') and
916 source_file_filter(f))
917 python_changed_files = [
918 f.LocalPath()
919 for f in input_api.AffectedFiles(include_deletes=False,
920 file_filter=python_file_filter)
921 ]
Byoungchan Lee94f2ef22021-07-01 13:21:44922
Christoffer Jansson4e8a7732022-02-08 08:01:12923 results.extend(
924 input_api.canned_checks.RunPylint(
925 input_api,
926 output_api,
927 files_to_check=python_changed_files,
928 files_to_skip=(
929 r'^base[\\\/].*\.py$',
930 r'^build[\\\/].*\.py$',
931 r'^buildtools[\\\/].*\.py$',
932 r'^infra[\\\/].*\.py$',
933 r'^ios[\\\/].*\.py$',
934 r'^out.*[\\\/].*\.py$',
935 r'^testing[\\\/].*\.py$',
936 r'^third_party[\\\/].*\.py$',
937 r'^tools[\\\/].*\.py$',
Christoffer Jansson4e8a7732022-02-08 08:01:12938 r'^xcodebuild.*[\\\/].*\.py$',
939 ),
940 pylintrc='pylintrc',
941 version='2.7'))
kjellander569cf942016-02-11 13:02:59942
Christoffer Jansson4e8a7732022-02-08 08:01:12943 # TODO(bugs.webrtc.org/13606): talk/ is no more, so make below checks simpler?
944 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function
945 # since we need to have different license checks
946 # in talk/ and webrtc/directories.
947 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 06:03:56948
Christoffer Jansson4e8a7732022-02-08 08:01:12949 # .m and .mm files are ObjC files. For simplicity we will consider
950 # .h files in ObjC subdirectories ObjC headers.
951 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
952 # Skip long-lines check for DEPS and GN files.
953 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
954 # Also we will skip most checks for third_party directory.
Byoungchan Lee81d65fc2022-05-12 13:48:05955 third_party_filter_list = (r'(^|.*[\\\/])third_party[\\\/].+', )
Christoffer Jansson4e8a7732022-02-08 08:01:12956 eighty_char_sources = lambda x: input_api.FilterSourceFile(
957 x,
958 files_to_skip=build_file_filter_list + objc_filter_list +
959 third_party_filter_list)
960 hundred_char_sources = lambda x: input_api.FilterSourceFile(
961 x, files_to_check=objc_filter_list)
962 non_third_party_sources = lambda x: input_api.FilterSourceFile(
963 x, files_to_skip=third_party_filter_list)
Artem Titove92675b2018-05-22 08:21:27964
Christoffer Jansson4e8a7732022-02-08 08:01:12965 results.extend(
966 input_api.canned_checks.CheckLongLines(
967 input_api,
968 output_api,
969 maxlen=80,
970 source_file_filter=eighty_char_sources))
971 results.extend(
972 input_api.canned_checks.CheckLongLines(
973 input_api,
974 output_api,
975 maxlen=100,
976 source_file_filter=hundred_char_sources))
977 results.extend(
978 input_api.canned_checks.CheckChangeHasNoTabs(
979 input_api, output_api, source_file_filter=non_third_party_sources))
980 results.extend(
981 input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
982 input_api, output_api, source_file_filter=non_third_party_sources))
983 results.extend(
984 input_api.canned_checks.CheckAuthorizedAuthor(
985 input_api,
986 output_api,
987 bot_allowlist=[
988 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com',
989 'webrtc-version-updater@webrtc-ci.iam.gserviceaccount.com',
990 ]))
991 results.extend(
992 input_api.canned_checks.CheckChangeTodoHasOwner(
993 input_api, output_api, source_file_filter=non_third_party_sources))
994 results.extend(
995 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
996 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
997 results.extend(
998 CheckNoIOStreamInHeaders(input_api,
999 output_api,
1000 source_file_filter=non_third_party_sources))
1001 results.extend(
1002 CheckNoPragmaOnce(input_api,
1003 output_api,
1004 source_file_filter=non_third_party_sources))
1005 results.extend(
1006 CheckNoFRIEND_TEST(input_api,
1007 output_api,
1008 source_file_filter=non_third_party_sources))
1009 results.extend(CheckGnChanges(input_api, output_api))
1010 results.extend(
1011 CheckUnwantedDependencies(input_api,
1012 output_api,
1013 source_file_filter=non_third_party_sources))
1014 results.extend(
1015 CheckJSONParseErrors(input_api,
Mirko Bonadei8cc66952020-10-30 09:13:451016 output_api,
1017 source_file_filter=non_third_party_sources))
Christoffer Jansson4e8a7732022-02-08 08:01:121018 results.extend(RunPythonTests(input_api, output_api))
1019 results.extend(
1020 CheckUsageOfGoogleProtobufNamespace(
1021 input_api, output_api, source_file_filter=non_third_party_sources))
1022 results.extend(
1023 CheckOrphanHeaders(input_api,
1024 output_api,
1025 source_file_filter=non_third_party_sources))
1026 results.extend(
1027 CheckNewlineAtTheEndOfProtoFiles(
1028 input_api, output_api, source_file_filter=non_third_party_sources))
1029 results.extend(
1030 CheckNoStreamUsageIsAdded(input_api, output_api, non_third_party_sources))
1031 results.extend(
1032 CheckNoTestCaseUsageIsAdded(input_api, output_api,
Mirko Bonadei8cc66952020-10-30 09:13:451033 non_third_party_sources))
Christoffer Jansson4e8a7732022-02-08 08:01:121034 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
1035 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
1036 results.extend(
1037 CheckAbslMemoryInclude(input_api, output_api, non_third_party_sources))
1038 results.extend(
1039 CheckAssertUsage(input_api, output_api, non_third_party_sources))
1040 results.extend(
1041 CheckBannedAbslMakeUnique(input_api, output_api, non_third_party_sources))
1042 results.extend(
1043 CheckObjcApiSymbols(input_api, output_api, non_third_party_sources))
1044 return results
Mirko Bonadeia418e672018-10-24 11:57:251045
1046
1047def CheckApiDepsFileIsUpToDate(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:121048 """Check that 'include_rules' in api/DEPS is up to date.
Mirko Bonadei90490372018-10-26 11:17:471049
1050 The file api/DEPS must be kept up to date in order to avoid to avoid to
1051 include internal header from WebRTC's api/ headers.
1052
1053 This check is focused on ensuring that 'include_rules' contains a deny
1054 rule for each root level directory. More focused allow rules can be
1055 added to 'specific_include_rules'.
1056 """
Christoffer Jansson4e8a7732022-02-08 08:01:121057 results = []
1058 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
1059 with open(api_deps) as f:
1060 deps_content = _ParseDeps(f.read())
Mirko Bonadeia418e672018-10-24 11:57:251061
Christoffer Jansson4e8a7732022-02-08 08:01:121062 include_rules = deps_content.get('include_rules', [])
1063 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 11:57:251064
Christoffer Jansson4e8a7732022-02-08 08:01:121065 # Only check top level directories affected by the current CL.
1066 dirs_to_check = set()
1067 for f in input_api.AffectedFiles():
1068 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
1069 if len(path_tokens) > 1:
1070 if (path_tokens[0] not in dirs_to_skip and os.path.isdir(
1071 os.path.join(input_api.PresubmitLocalPath(), path_tokens[0]))):
1072 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 11:57:251073
Christoffer Jansson4e8a7732022-02-08 08:01:121074 missing_include_rules = set()
1075 for p in dirs_to_check:
1076 rule = '-%s' % p
1077 if rule not in include_rules:
1078 missing_include_rules.add(rule)
Mirko Bonadei90490372018-10-26 11:17:471079
Christoffer Jansson4e8a7732022-02-08 08:01:121080 if missing_include_rules:
1081 error_msg = [
1082 'include_rules = [\n',
1083 ' ...\n',
1084 ]
Mirko Bonadeia418e672018-10-24 11:57:251085
Christoffer Jansson4e8a7732022-02-08 08:01:121086 for r in sorted(missing_include_rules):
1087 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 11:57:251088
Christoffer Jansson4e8a7732022-02-08 08:01:121089 error_msg.append(' ...\n')
1090 error_msg.append(']\n')
Mirko Bonadei90490372018-10-26 11:17:471091
Christoffer Jansson4e8a7732022-02-08 08:01:121092 results.append(
1093 output_api.PresubmitError(
1094 'New root level directory detected! WebRTC api/ headers should '
1095 'not #include headers from \n'
1096 'the new directory, so please update "include_rules" in file\n'
1097 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
Mirko Bonadei90490372018-10-26 11:17:471098
Christoffer Jansson4e8a7732022-02-08 08:01:121099 return results
Mirko Bonadei8cc66952020-10-30 09:13:451100
andrew@webrtc.org2442de12012-01-23 17:45:411101
Mirko Bonadei9fa8ef12019-09-17 17:14:131102def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:121103 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
1104 source_file_filter(f))
Mirko Bonadei9fa8ef12019-09-17 17:14:131105
Christoffer Jansson4e8a7732022-02-08 08:01:121106 files = []
1107 for f in input_api.AffectedFiles(include_deletes=False,
1108 file_filter=file_filter):
1109 for _, line in f.ChangedContents():
1110 if 'absl::make_unique' in line:
1111 files.append(f)
1112 break
Mirko Bonadei9fa8ef12019-09-17 17:14:131113
Christoffer Jansson4e8a7732022-02-08 08:01:121114 if files:
1115 return [
1116 output_api.PresubmitError(
1117 'Please use std::make_unique instead of absl::make_unique.\n'
1118 'Affected files:', files)
1119 ]
1120 return []
Mirko Bonadei8cc66952020-10-30 09:13:451121
Mirko Bonadei9fa8ef12019-09-17 17:14:131122
Mirko Bonadeid74c0e62020-07-16 19:57:011123def CheckObjcApiSymbols(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:121124 rtc_objc_export = re.compile(r'RTC_OBJC_EXPORT(.|\n){26}',
1125 re.MULTILINE | re.DOTALL)
1126 file_filter = lambda f: (f.LocalPath().endswith(('.h')) and
1127 source_file_filter(f))
Mirko Bonadeid74c0e62020-07-16 19:57:011128
Christoffer Jansson4e8a7732022-02-08 08:01:121129 files = []
1130 file_filter = lambda x: (input_api.FilterSourceFile(x) and source_file_filter(
1131 x))
1132 for f in input_api.AffectedSourceFiles(file_filter):
1133 if not f.LocalPath().endswith('.h') or not 'sdk/objc' in f.LocalPath():
1134 continue
1135 if f.LocalPath().endswith('sdk/objc/base/RTCMacros.h'):
1136 continue
1137 contents = input_api.ReadFile(f)
1138 for match in rtc_objc_export.finditer(contents):
1139 export_block = match.group(0)
1140 if 'RTC_OBJC_TYPE' not in export_block:
1141 files.append(f.LocalPath())
Mirko Bonadeid74c0e62020-07-16 19:57:011142
Christoffer Jansson4e8a7732022-02-08 08:01:121143 if len(files) > 0:
1144 return [
1145 output_api.PresubmitError(
1146 'RTC_OBJC_EXPORT types must be wrapped into an RTC_OBJC_TYPE() ' +
1147 'macro.\n\n' + 'For example:\n' +
1148 'RTC_OBJC_EXPORT @protocol RTC_OBJC_TYPE(RtcFoo)\n\n' +
1149 'RTC_OBJC_EXPORT @interface RTC_OBJC_TYPE(RtcFoo)\n\n' +
1150 'Please fix the following files:', files)
1151 ]
1152 return []
Mirko Bonadei8cc66952020-10-30 09:13:451153
Mirko Bonadeid74c0e62020-07-16 19:57:011154
Mirko Bonadeia6395132021-07-22 15:35:591155def CheckAssertUsage(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:121156 pattern = input_api.re.compile(r'\bassert\(')
1157 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h', '.m', '.mm'))
1158 and source_file_filter(f))
Mirko Bonadeia6395132021-07-22 15:35:591159
Christoffer Jansson4e8a7732022-02-08 08:01:121160 files = []
1161 for f in input_api.AffectedFiles(include_deletes=False,
1162 file_filter=file_filter):
1163 for _, line in f.ChangedContents():
1164 if pattern.search(line):
1165 files.append(f.LocalPath())
1166 break
Mirko Bonadeia6395132021-07-22 15:35:591167
Christoffer Jansson4e8a7732022-02-08 08:01:121168 if len(files) > 0:
1169 return [
1170 output_api.PresubmitError(
1171 'Usage of assert() has been detected in the following files, '
1172 'please use RTC_DCHECK() instead.\n Files:', files)
1173 ]
1174 return []
Mirko Bonadeia6395132021-07-22 15:35:591175
1176
tzika06bf852018-11-15 11:37:351177def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:121178 pattern = input_api.re.compile(r'^#include\s*"absl/memory/memory.h"',
1179 input_api.re.MULTILINE)
1180 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h')) and
1181 source_file_filter(f))
tzika06bf852018-11-15 11:37:351182
Christoffer Jansson4e8a7732022-02-08 08:01:121183 files = []
1184 for f in input_api.AffectedFiles(include_deletes=False,
1185 file_filter=file_filter):
1186 contents = input_api.ReadFile(f)
1187 if pattern.search(contents):
1188 continue
1189 for _, line in f.ChangedContents():
1190 if 'absl::WrapUnique' in line:
1191 files.append(f)
1192 break
tzika06bf852018-11-15 11:37:351193
Christoffer Jansson4e8a7732022-02-08 08:01:121194 if len(files) > 0:
1195 return [
1196 output_api.PresubmitError(
1197 'Please include "absl/memory/memory.h" header for '
1198 'absl::WrapUnique.\nThis header may or may not be included '
1199 'transitively depending on the C++ standard version.', files)
1200 ]
1201 return []
Mirko Bonadei8cc66952020-10-30 09:13:451202
kjellander@webrtc.orge4158642014-08-06 09:11:181203
andrew@webrtc.org53df1362012-01-26 21:24:231204def CheckChangeOnUpload(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:121205 results = []
1206 results.extend(CommonChecks(input_api, output_api))
1207 results.extend(CheckGnGen(input_api, output_api))
1208 results.extend(input_api.canned_checks.CheckGNFormatted(
1209 input_api, output_api))
1210 return results
niklase@google.comda159d62011-05-30 11:51:341211
kjellander@webrtc.orge4158642014-08-06 09:11:181212
andrew@webrtc.org2442de12012-01-23 17:45:411213def CheckChangeOnCommit(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:121214 results = []
1215 results.extend(CommonChecks(input_api, output_api))
1216 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
1217 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
1218 results.extend(
1219 input_api.canned_checks.CheckChangeWasUploaded(input_api, output_api))
1220 results.extend(
1221 input_api.canned_checks.CheckChangeHasDescription(input_api, output_api))
1222 results.extend(CheckChangeHasBugField(input_api, output_api))
1223 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
1224 results.extend(
1225 input_api.canned_checks.CheckTreeIsOpen(
1226 input_api,
1227 output_api,
1228 json_url='http://webrtc-status.appspot.com/current?format=json'))
1229 return results
mbonadei74973ed2017-05-09 14:58:051230
1231
Artem Titova04d1402018-05-11 09:23:001232def CheckOrphanHeaders(input_api, output_api, source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:121233 # We need to wait until we have an input_api object and use this
1234 # roundabout construct to import prebubmit_checks_lib because this file is
1235 # eval-ed and thus doesn't have __file__.
1236 error_msg = """{} should be listed in {}."""
1237 results = []
Christoffer Jansson884e8ae2022-02-11 20:29:381238 exempt_paths = [re.escape(os.path.join('tools_webrtc', 'ios', 'SDK'))]
1239
Christoffer Jansson4e8a7732022-02-08 08:01:121240 with _AddToPath(
1241 input_api.os_path.join(input_api.PresubmitLocalPath(), 'tools_webrtc',
1242 'presubmit_checks_lib')):
1243 from check_orphan_headers import GetBuildGnPathFromFilePath
1244 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 14:58:051245
Christoffer Jansson4e8a7732022-02-08 08:01:121246 file_filter = lambda x: input_api.FilterSourceFile(
1247 x, files_to_skip=exempt_paths) and source_file_filter(x)
1248 for f in input_api.AffectedSourceFiles(file_filter):
1249 if f.LocalPath().endswith('.h'):
1250 file_path = os.path.abspath(f.LocalPath())
1251 root_dir = os.getcwd()
1252 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
1253 root_dir)
1254 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1255 if not in_build_gn:
1256 results.append(
1257 output_api.PresubmitError(
1258 error_msg.format(f.LocalPath(), os.path.relpath(gn_file_path))))
1259 return results
Mirko Bonadei960fd5b2017-06-29 12:59:361260
1261
Mirko Bonadei8cc66952020-10-30 09:13:451262def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api,
1263 source_file_filter):
Christoffer Jansson4e8a7732022-02-08 08:01:121264 """Checks that all .proto files are terminated with a newline."""
1265 error_msg = 'File {} must end with exactly one newline.'
1266 results = []
1267 file_filter = lambda x: input_api.FilterSourceFile(
1268 x, files_to_check=(r'.+\.proto$', )) and source_file_filter(x)
1269 for f in input_api.AffectedSourceFiles(file_filter):
1270 file_path = f.LocalPath()
1271 with open(file_path) as f:
1272 lines = f.readlines()
1273 if len(lines) > 0 and not lines[-1].endswith('\n'):
1274 results.append(output_api.PresubmitError(error_msg.format(file_path)))
1275 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231276
1277
1278def _ExtractAddRulesFromParsedDeps(parsed_deps):
Christoffer Jansson4e8a7732022-02-08 08:01:121279 """Extract the rules that add dependencies from a parsed DEPS file.
Mirko Bonadei7e4ee6e2018-09-28 09:45:231280
1281 Args:
1282 parsed_deps: the locals dictionary from evaluating the DEPS file."""
Christoffer Jansson4e8a7732022-02-08 08:01:121283 add_rules = set()
1284 add_rules.update([
1285 rule[1:] for rule in parsed_deps.get('include_rules', [])
1286 if rule.startswith('+') or rule.startswith('!')
1287 ])
1288 for _, rules in parsed_deps.get('specific_include_rules', {}).items():
Mirko Bonadei7e4ee6e2018-09-28 09:45:231289 add_rules.update([
Christoffer Jansson4e8a7732022-02-08 08:01:121290 rule[1:] for rule in rules
Mirko Bonadei7e4ee6e2018-09-28 09:45:231291 if rule.startswith('+') or rule.startswith('!')
1292 ])
Christoffer Jansson4e8a7732022-02-08 08:01:121293 return add_rules
Mirko Bonadei7e4ee6e2018-09-28 09:45:231294
1295
1296def _ParseDeps(contents):
Christoffer Jansson4e8a7732022-02-08 08:01:121297 """Simple helper for parsing DEPS files."""
Mirko Bonadei7e4ee6e2018-09-28 09:45:231298
Christoffer Jansson4e8a7732022-02-08 08:01:121299 # Stubs for handling special syntax in the root DEPS file.
1300 class VarImpl:
1301 def __init__(self, local_scope):
1302 self._local_scope = local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231303
Christoffer Jansson4e8a7732022-02-08 08:01:121304 def Lookup(self, var_name):
1305 """Implements the Var syntax."""
1306 try:
1307 return self._local_scope['vars'][var_name]
1308 except KeyError as var_not_defined:
1309 raise Exception('Var is not defined: %s' %
1310 var_name) from var_not_defined
Mirko Bonadei7e4ee6e2018-09-28 09:45:231311
Christoffer Jansson4e8a7732022-02-08 08:01:121312 local_scope = {}
1313 global_scope = {
1314 'Var': VarImpl(local_scope).Lookup,
1315 }
1316 exec(contents, global_scope, local_scope)
1317 return local_scope
Mirko Bonadei7e4ee6e2018-09-28 09:45:231318
1319
1320def _CalculateAddedDeps(os_path, old_contents, new_contents):
Christoffer Jansson4e8a7732022-02-08 08:01:121321 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
Mirko Bonadei7e4ee6e2018-09-28 09:45:231322 a set of DEPS entries that we should look up.
1323
1324 For a directory (rather than a specific filename) we fake a path to
1325 a specific filename by adding /DEPS. This is chosen as a file that
1326 will seldom or never be subject to per-file include_rules.
1327 """
Christoffer Jansson4e8a7732022-02-08 08:01:121328 # We ignore deps entries on auto-generated directories.
1329 auto_generated_dirs = ['grit', 'jni']
Mirko Bonadei7e4ee6e2018-09-28 09:45:231330
Christoffer Jansson4e8a7732022-02-08 08:01:121331 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1332 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231333
Christoffer Jansson4e8a7732022-02-08 08:01:121334 added_deps = new_deps.difference(old_deps)
Mirko Bonadei7e4ee6e2018-09-28 09:45:231335
Christoffer Jansson4e8a7732022-02-08 08:01:121336 results = set()
1337 for added_dep in added_deps:
1338 if added_dep.split('/')[0] in auto_generated_dirs:
1339 continue
1340 # Assume that a rule that ends in .h is a rule for a specific file.
1341 if added_dep.endswith('.h'):
1342 results.add(added_dep)
1343 else:
1344 results.add(os_path.join(added_dep, 'DEPS'))
1345 return results
Mirko Bonadei7e4ee6e2018-09-28 09:45:231346
1347
1348def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
Christoffer Jansson4e8a7732022-02-08 08:01:121349 """When a dependency prefixed with + is added to a DEPS file, we
Edward Lesmesbef08502021-02-05 22:08:321350 want to make sure that the change is reviewed by an OWNER of the
1351 target file or directory, to avoid layering violations from being
1352 introduced. This check verifies that this happens.
1353 """
Christoffer Jansson4e8a7732022-02-08 08:01:121354 virtual_depended_on_files = set()
Mirko Bonadei7e4ee6e2018-09-28 09:45:231355
Christoffer Jansson4e8a7732022-02-08 08:01:121356 file_filter = lambda f: not input_api.re.match(
1357 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1358 for f in input_api.AffectedFiles(include_deletes=False,
1359 file_filter=file_filter):
1360 filename = input_api.os_path.basename(f.LocalPath())
1361 if filename == 'DEPS':
1362 virtual_depended_on_files.update(
1363 _CalculateAddedDeps(input_api.os_path, '\n'.join(f.OldContents()),
1364 '\n'.join(f.NewContents())))
Mirko Bonadei7e4ee6e2018-09-28 09:45:231365
Christoffer Jansson4e8a7732022-02-08 08:01:121366 if not virtual_depended_on_files:
Mirko Bonadei8cc66952020-10-30 09:13:451367 return []
Christoffer Jansson4e8a7732022-02-08 08:01:121368
1369 if input_api.is_committing:
1370 if input_api.tbr:
1371 return [
1372 output_api.PresubmitNotifyResult(
1373 '--tbr was specified, skipping OWNERS check for DEPS '
1374 'additions')
1375 ]
1376 if input_api.dry_run:
1377 return [
1378 output_api.PresubmitNotifyResult(
1379 'This is a dry run, skipping OWNERS check for DEPS '
1380 'additions')
1381 ]
1382 if not input_api.change.issue:
1383 return [
1384 output_api.PresubmitError(
1385 "DEPS approval by OWNERS check failed: this change has "
1386 "no change number, so we can't check it for approvals.")
1387 ]
1388 output = output_api.PresubmitError
1389 else:
1390 output = output_api.PresubmitNotifyResult
1391
1392 owner_email, reviewers = (
1393 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1394 input_api, None, approval_needed=input_api.is_committing))
1395
1396 owner_email = owner_email or input_api.change.author_email
1397
1398 approval_status = input_api.owners_client.GetFilesApprovalStatus(
1399 virtual_depended_on_files, reviewers.union([owner_email]), [])
1400 missing_files = [
1401 f for f in virtual_depended_on_files
1402 if approval_status[f] != input_api.owners_client.APPROVED
1403 ]
1404
1405 # We strip the /DEPS part that was added by
1406 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1407 # directory.
1408 def StripDeps(path):
1409 start_deps = path.rfind('/DEPS')
1410 if start_deps != -1:
1411 return path[:start_deps]
1412 return path
1413
1414 unapproved_dependencies = [
1415 "'+%s'," % StripDeps(path) for path in missing_files
1416 ]
1417
1418 if unapproved_dependencies:
1419 output_list = [
1420 output('You need LGTM from owners of depends-on paths in DEPS that '
1421 ' were modified in this CL:\n %s' %
1422 '\n '.join(sorted(unapproved_dependencies)))
1423 ]
1424 suggested_owners = input_api.owners_client.SuggestOwners(
1425 missing_files, exclude=[owner_email])
1426 output_list.append(
1427 output('Suggested missing target path OWNERS:\n %s' %
1428 '\n '.join(suggested_owners or [])))
1429 return output_list
1430
1431 return []