blob: e1c9b8e92e2768ffc9aba7e2e50352a6d449250a [file] [log] [blame]
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
2#
3# Use of this source code is governed by a BSD-style license
4# that can be found in the LICENSE file in the root of the source
5# tree. An additional intellectual property rights grant can be found
6# in the file PATENTS. All contributing project authors may
7# be found in the AUTHORS file in the root of the source tree.
niklase@google.comda159d62011-05-30 11:51:34 +00008
kjellander7439f972016-12-05 22:47:46 -08009import json
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +000010import os
kjellander@webrtc.org85759802013-10-22 16:47:40 +000011import re
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000012import sys
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020013from collections import defaultdict
Oleh Prypin2f33a562017-10-04 20:17:54 +020014from contextlib import contextmanager
kjellander@webrtc.org85759802013-10-22 16:47:40 +000015
oprypin2aa463f2017-03-23 03:17:02 -070016# Files and directories that are *skipped* by cpplint in the presubmit script.
17CPPLINT_BLACKLIST = [
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018 'api/video_codecs/video_decoder.h',
19 'common_types.cc',
20 'common_types.h',
21 'examples/objc',
Amit Hilbuchce7032b2019-01-22 16:19:35 -080022 'media/base/stream_params.h',
23 'media/base/video_common.h',
24 'media/sctp/sctp_transport.cc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025 'modules/audio_coding',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026 'modules/audio_device',
27 'modules/audio_processing',
28 'modules/desktop_capture',
29 'modules/include/module_common_types.h',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020030 'modules/utility',
31 'modules/video_capture',
Amit Hilbuchce7032b2019-01-22 16:19:35 -080032 'p2p/base/pseudo_tcp.cc',
33 'p2p/base/pseudo_tcp.h',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020034 'rtc_base',
35 'sdk/android/src/jni',
36 'sdk/objc',
37 'system_wrappers',
38 'test',
Henrik Kjellander90fd7d82017-05-09 08:30:10 +020039 'tools_webrtc',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020040 'voice_engine',
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +010041]
42
jbauchc4e3ead2016-02-19 00:25:55 -080043# These filters will always be removed, even if the caller specifies a filter
44# set, as they are problematic or broken in some way.
45#
46# Justifications for each filter:
47# - build/c++11 : Rvalue ref checks are unreliable (false positives),
48# include file and feature blacklists are
49# google3-specific.
Mirko Bonadeie92e2862020-05-29 15:23:09 +020050# - runtime/references : Mutable references are not banned by the Google
51# C++ style guide anymore (starting from May 2020).
kjellandere5a87a52016-04-27 02:32:12 -070052# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
53# all move-related errors).
jbauchc4e3ead2016-02-19 00:25:55 -080054BLACKLIST_LINT_FILTERS = [
55 '-build/c++11',
Mirko Bonadeie92e2862020-05-29 15:23:09 +020056 '-runtime/references',
kjellandere5a87a52016-04-27 02:32:12 -070057 '-whitespace/operators',
jbauchc4e3ead2016-02-19 00:25:55 -080058]
59
kjellanderfd595232015-12-04 02:44:09 -080060# List of directories of "supported" native APIs. That means changes to headers
61# will be done in a compatible way following this scheme:
62# 1. Non-breaking changes are made.
63# 2. The old APIs as marked as deprecated (with comments).
64# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
65# webrtc-users@google.com (internal list).
66# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-02 23:56:14 -080067NATIVE_API_DIRS = (
Karl Wibergef52d8b82017-10-25 13:20:03 +020068 'api', # All subdirectories of api/ are included as well.
Mirko Bonadeia4eeeff2018-01-11 13:16:52 +010069 'media/base',
70 'media/engine',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020071 'modules/audio_device/include',
72 'pc',
kjellanderdd705472016-06-09 11:17:27 -070073)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020074
kjellanderdd705472016-06-09 11:17:27 -070075# These directories should not be used but are maintained only to avoid breaking
76# some legacy downstream code.
77LEGACY_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020078 'common_audio/include',
79 'modules/audio_coding/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020080 'modules/audio_processing/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020081 'modules/congestion_controller/include',
82 'modules/include',
83 'modules/remote_bitrate_estimator/include',
84 'modules/rtp_rtcp/include',
85 'modules/rtp_rtcp/source',
86 'modules/utility/include',
87 'modules/video_coding/codecs/h264/include',
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020088 'modules/video_coding/codecs/vp8/include',
89 'modules/video_coding/codecs/vp9/include',
90 'modules/video_coding/include',
91 'rtc_base',
92 'system_wrappers/include',
kjellander53047c92015-12-02 23:56:14 -080093)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020094
Karl Wibergd4f01c12017-11-10 10:55:45 +010095# NOTE: The set of directories in API_DIRS should be the same as those
96# listed in the table in native-api.md.
kjellanderdd705472016-06-09 11:17:27 -070097API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080098
Mirko Bonadei4dc4e252017-09-19 13:49:16 +020099# TARGET_RE matches a GN target, and extracts the target name and the contents.
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200100TARGET_RE = re.compile(
101 r'(?P<indent>\s*)(?P<target_type>\w+)\("(?P<target_name>\w+)"\) {'
102 r'(?P<target_contents>.*?)'
103 r'(?P=indent)}',
104 re.MULTILINE | re.DOTALL)
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200105
106# SOURCES_RE matches a block of sources inside a GN target.
107SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
108 re.MULTILINE | re.DOTALL)
109
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200110# DEPS_RE matches a block of sources inside a GN target.
111DEPS_RE = re.compile(r'\bdeps \+?= \[(?P<deps>.*?)\]',
112 re.MULTILINE | re.DOTALL)
113
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200114# FILE_PATH_RE matchies a file path.
115FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
116
kjellander53047c92015-12-02 23:56:14 -0800117
Mirko Bonadeid8665442018-09-04 12:17:27 +0200118def FindSrcDirPath(starting_dir):
119 """Returns the abs path to the src/ dir of the project."""
120 src_dir = starting_dir
121 while os.path.basename(src_dir) != 'src':
122 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
123 return src_dir
124
125
Oleh Prypin2f33a562017-10-04 20:17:54 +0200126@contextmanager
127def _AddToPath(*paths):
128 original_sys_path = sys.path
129 sys.path.extend(paths)
130 try:
131 yield
132 finally:
133 # Restore sys.path to what it was before.
134 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 05:27:22 -0800135
136
charujain9893e252017-09-14 13:33:22 +0200137def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800138 """Ensures the list of native API header directories is up to date."""
139 non_existing_paths = []
140 native_api_full_paths = [
141 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700142 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800143 for path in native_api_full_paths:
144 if not os.path.isdir(path):
145 non_existing_paths.append(path)
146 if non_existing_paths:
147 return [output_api.PresubmitError(
148 'Directories to native API headers have changed which has made the '
149 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
150 'location of our native APIs.',
151 non_existing_paths)]
152 return []
153
Artem Titove92675b2018-05-22 10:21:27 +0200154
kjellanderc88b5d52017-04-05 06:42:43 -0700155API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700156You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800157 1. Make compatible changes that don't break existing clients. Usually
158 this is done by keeping the existing method signatures unchanged.
159 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700160 3. Create a timeline and plan for when the deprecated stuff will be
161 removed. (The amount of time we give users to change their code
162 should be informed by how much work it is for them. If they just
163 need to replace one name with another or something equally
164 simple, 1-2 weeks might be good; if they need to do serious work,
165 up to 3 months may be called for.)
166 4. Update/inform existing downstream code owners to stop using the
167 deprecated stuff. (Send announcements to
168 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
169 5. Remove the deprecated stuff, once the agreed-upon amount of time
170 has passed.
171Related files:
172"""
kjellander53047c92015-12-02 23:56:14 -0800173
Artem Titove92675b2018-05-22 10:21:27 +0200174
charujain9893e252017-09-14 13:33:22 +0200175def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800176 """Checks to remind proper changing of native APIs."""
177 files = []
Karl Wiberg6bfac032017-10-27 15:14:20 +0200178 source_file_filter = lambda x: input_api.FilterSourceFile(
179 x, white_list=[r'.+\.(gn|gni|h)$'])
180 for f in input_api.AffectedSourceFiles(source_file_filter):
181 for path in API_DIRS:
182 dn = os.path.dirname(f.LocalPath())
183 if path == 'api':
184 # Special case: Subdirectories included.
185 if dn == 'api' or dn.startswith('api/'):
Niels Möller1d201852019-06-26 12:58:27 +0200186 files.append(f.LocalPath())
Karl Wiberg6bfac032017-10-27 15:14:20 +0200187 else:
188 # Normal case: Subdirectories not included.
189 if dn == path:
Niels Möller1d201852019-06-26 12:58:27 +0200190 files.append(f.LocalPath())
kjellander53047c92015-12-02 23:56:14 -0800191
192 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700193 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800194 return []
195
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100196
Artem Titova04d1402018-05-11 11:23:00 +0200197def CheckNoIOStreamInHeaders(input_api, output_api,
198 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000199 """Checks to make sure no .h files include <iostream>."""
200 files = []
201 pattern = input_api.re.compile(r'^#include\s*<iostream>',
202 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200203 file_filter = lambda x: (input_api.FilterSourceFile(x)
204 and source_file_filter(x))
205 for f in input_api.AffectedSourceFiles(file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000206 if not f.LocalPath().endswith('.h'):
207 continue
208 contents = input_api.ReadFile(f)
209 if pattern.search(contents):
210 files.append(f)
211
212 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200213 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000214 'Do not #include <iostream> in header files, since it inserts static ' +
215 'initialization into every file including the header. Instead, ' +
216 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200217 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000218 return []
219
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000220
Artem Titova04d1402018-05-11 11:23:00 +0200221def CheckNoPragmaOnce(input_api, output_api,
222 source_file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800223 """Make sure that banned functions are not used."""
224 files = []
225 pattern = input_api.re.compile(r'^#pragma\s+once',
226 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200227 file_filter = lambda x: (input_api.FilterSourceFile(x)
228 and source_file_filter(x))
229 for f in input_api.AffectedSourceFiles(file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800230 if not f.LocalPath().endswith('.h'):
231 continue
232 contents = input_api.ReadFile(f)
233 if pattern.search(contents):
234 files.append(f)
235
236 if files:
237 return [output_api.PresubmitError(
238 'Do not use #pragma once in header files.\n'
239 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
240 files)]
241 return []
242
243
Artem Titova04d1402018-05-11 11:23:00 +0200244def CheckNoFRIEND_TEST(input_api, output_api, # pylint: disable=invalid-name
245 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000246 """Make sure that gtest's FRIEND_TEST() macro is not used, the
247 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
248 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
249 problems = []
250
Artem Titova04d1402018-05-11 11:23:00 +0200251 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
252 and source_file_filter(f))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000253 for f in input_api.AffectedFiles(file_filter=file_filter):
254 for line_num, line in f.ChangedContents():
255 if 'FRIEND_TEST(' in line:
256 problems.append(' %s:%d' % (f.LocalPath(), line_num))
257
258 if not problems:
259 return []
260 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
261 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
262 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
263
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000264
charujain9893e252017-09-14 13:33:22 +0200265def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700266 """ Checks if a file is blacklisted for lint check."""
267 for path in blacklist_paths:
268 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100269 return True
270 return False
271
272
charujain9893e252017-09-14 13:33:22 +0200273def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 11:23:00 +0200274 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700275 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200276 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000277 depot_tools/presubmit_canned_checks.py but has less filters and only checks
278 added files."""
279 result = []
280
281 # Initialize cpplint.
282 import cpplint
283 # Access to a protected member _XX of a client class
284 # pylint: disable=W0212
285 cpplint._cpplint_state.ResetErrorCounts()
286
jbauchc4e3ead2016-02-19 00:25:55 -0800287 lint_filters = cpplint._Filters()
288 lint_filters.extend(BLACKLIST_LINT_FILTERS)
289 cpplint._SetFilters(','.join(lint_filters))
290
oprypin2aa463f2017-03-23 03:17:02 -0700291 # Create a platform independent blacklist for cpplint.
292 blacklist_paths = [input_api.os_path.join(*path.split('/'))
293 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100294
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000295 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700296 # default when running cpplint.py from command line. To make it possible to
297 # work with not-yet-converted code, we're only applying it to new (or
298 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000299 verbosity_level = 1
300 files = []
301 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200302 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200303 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
Artem Titove92675b2018-05-22 10:21:27 +0200304 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000305 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000306
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000307 for file_name in files:
308 cpplint.ProcessFile(file_name, verbosity_level)
309
310 if cpplint._cpplint_state.error_count > 0:
311 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700312 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000313 else:
314 res_type = output_api.PresubmitPromptWarning
315 result = [res_type('Changelist failed cpplint.py check.')]
316
317 return result
318
Artem Titove92675b2018-05-22 10:21:27 +0200319
charujain9893e252017-09-14 13:33:22 +0200320def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700321 # Disallow referencing source files with paths above the GN file location.
322 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
323 re.MULTILINE | re.DOTALL)
324 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
325 violating_gn_files = set()
326 violating_source_entries = []
327 for gn_file in gn_files:
328 contents = input_api.ReadFile(gn_file)
329 for source_block_match in source_pattern.finditer(contents):
330 # Find all source list entries starting with ../ in the source block
331 # (exclude overrides entries).
332 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
333 source_file = file_list_match.group(1)
334 if 'overrides/' not in source_file:
335 violating_source_entries.append(source_file)
336 violating_gn_files.add(gn_file)
337 if violating_gn_files:
338 return [output_api.PresubmitError(
339 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100340 'allowed. Please introduce new GN targets in the proper location '
341 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700342 'Invalid source entries:\n'
343 '%s\n'
344 'Violating GN files:' % '\n'.join(violating_source_entries),
345 items=violating_gn_files)]
346 return []
347
Artem Titove92675b2018-05-22 10:21:27 +0200348
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200349def CheckAbseilDependencies(input_api, gn_files, output_api):
350 """Checks that Abseil dependencies are declared in `absl_deps`."""
351 absl_re = re.compile(r'third_party/abseil-cpp', re.MULTILINE | re.DOTALL)
352 target_types_to_check = [
353 'rtc_library',
354 'rtc_source_set',
Mirko Bonadei96115cf2020-06-23 23:39:56 +0200355 'rtc_static_library',
356 'webrtc_fuzzer_test',
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200357 ]
358 error_msg = ('Abseil dependencies in target "%s" (file: %s) '
359 'should be moved to the "absl_deps" parameter.')
360 errors = []
361
362 for gn_file in gn_files:
363 gn_file_content = input_api.ReadFile(gn_file)
364 for target_match in TARGET_RE.finditer(gn_file_content):
365 target_type = target_match.group('target_type')
366 target_name = target_match.group('target_name')
367 target_contents = target_match.group('target_contents')
368 if target_type in target_types_to_check:
369 for deps_match in DEPS_RE.finditer(target_contents):
370 deps = deps_match.group('deps').splitlines()
371 for dep in deps:
372 if re.search(absl_re, dep):
373 errors.append(
374 output_api.PresubmitError(error_msg % (target_name,
375 gn_file.LocalPath())))
376 break # no need to warn more than once per target
377 return errors
378
379
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200380def CheckNoMixingSources(input_api, gn_files, output_api):
381 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
382
383 See bugs.webrtc.org/7743 for more context.
384 """
Artem Titove92675b2018-05-22 10:21:27 +0200385
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200386 def _MoreThanOneSourceUsed(*sources_lists):
387 sources_used = 0
388 for source_list in sources_lists:
389 if len(source_list):
390 sources_used += 1
391 return sources_used > 1
392
393 errors = defaultdict(lambda: [])
kjellander7439f972016-12-05 22:47:46 -0800394 for gn_file in gn_files:
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200395 gn_file_content = input_api.ReadFile(gn_file)
396 for target_match in TARGET_RE.finditer(gn_file_content):
397 # list_of_sources is a list of tuples of the form
398 # (c_files, cc_files, objc_files) that keeps track of all the sources
399 # defined in a target. A GN target can have more that on definition of
400 # sources (since it supports if/else statements).
401 # E.g.:
402 # rtc_static_library("foo") {
403 # if (is_win) {
404 # sources = [ "foo.cc" ]
405 # } else {
406 # sources = [ "foo.mm" ]
407 # }
408 # }
409 # This is allowed and the presubmit check should support this case.
410 list_of_sources = []
kjellander7439f972016-12-05 22:47:46 -0800411 c_files = []
412 cc_files = []
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200413 objc_files = []
414 target_name = target_match.group('target_name')
415 target_contents = target_match.group('target_contents')
416 for sources_match in SOURCES_RE.finditer(target_contents):
417 if '+=' not in sources_match.group(0):
418 if c_files or cc_files or objc_files:
419 list_of_sources.append((c_files, cc_files, objc_files))
420 c_files = []
421 cc_files = []
422 objc_files = []
423 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
424 file_path = file_match.group('file_path')
425 extension = file_match.group('extension')
426 if extension == '.c':
427 c_files.append(file_path + extension)
428 if extension == '.cc':
429 cc_files.append(file_path + extension)
430 if extension in ['.m', '.mm']:
431 objc_files.append(file_path + extension)
432 list_of_sources.append((c_files, cc_files, objc_files))
433 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
434 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
435 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
436 errors[gn_file.LocalPath()].append((target_name, all_sources))
437 if errors:
kjellander7439f972016-12-05 22:47:46 -0800438 return [output_api.PresubmitError(
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200439 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
440 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-05 22:47:46 -0800441 'Mixed sources: \n'
442 '%s\n'
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200443 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
444 '\n'.join(errors.keys())))]
kjellander7439f972016-12-05 22:47:46 -0800445 return []
446
Artem Titove92675b2018-05-22 10:21:27 +0200447
charujain9893e252017-09-14 13:33:22 +0200448def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800449 cwd = input_api.PresubmitLocalPath()
Oleh Prypin2f33a562017-10-04 20:17:54 +0200450 with _AddToPath(input_api.os_path.join(
451 cwd, 'tools_webrtc', 'presubmit_checks_lib')):
452 from check_package_boundaries import CheckPackageBoundaries
453 build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
454 errors = CheckPackageBoundaries(cwd, build_files)[:5]
455 if errors:
ehmaldonado4fb97462017-01-30 05:27:22 -0800456 return [output_api.PresubmitError(
Oleh Prypin2f33a562017-10-04 20:17:54 +0200457 'There are package boundary violations in the following GN files:',
458 long_text='\n\n'.join(str(err) for err in errors))]
ehmaldonado4fb97462017-01-30 05:27:22 -0800459 return []
460
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100461
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200462def _ReportFileAndLine(filename, line_num):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100463 """Default error formatter for _FindNewViolationsOfRule."""
464 return '%s (line %s)' % (filename, line_num)
465
466
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200467def CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api,
468 error_formatter=_ReportFileAndLine):
469 """Make sure that warning suppression flags are not added wihtout a reason."""
470 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
471 'in WebRTC.\n'
472 'If you are not adding this code (e.g. you are just moving '
473 'existing code) or you want to add an exception,\n'
474 'you can add a comment on the line that causes the problem:\n\n'
475 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
476 '\n'
477 'Affected files:\n')
478 errors = [] # 2-element tuples with (file, line number)
479 clang_warn_re = input_api.re.compile(r'//build/config/clang:extra_warnings')
480 no_presubmit_re = input_api.re.compile(
481 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
482 for f in gn_files:
483 for line_num, line in f.ChangedContents():
484 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
485 errors.append(error_formatter(f.LocalPath(), line_num))
486 if errors:
487 return [output_api.PresubmitError(msg, errors)]
488 return []
489
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100490
491def CheckNoTestCaseUsageIsAdded(input_api, output_api, source_file_filter,
492 error_formatter=_ReportFileAndLine):
493 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
494 'new API: https://github.com/google/googletest/blob/master/'
495 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
496 'Affected files:\n')
497 errors = [] # 2-element tuples with (file, line number)
498 test_case_re = input_api.re.compile(r'TEST_CASE')
499 file_filter = lambda f: (source_file_filter(f)
500 and f.LocalPath().endswith('.cc'))
501 for f in input_api.AffectedSourceFiles(file_filter):
502 for line_num, line in f.ChangedContents():
503 if test_case_re.search(line):
504 errors.append(error_formatter(f.LocalPath(), line_num))
505 if errors:
506 return [output_api.PresubmitError(error_msg, errors)]
507 return []
508
509
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100510def CheckNoStreamUsageIsAdded(input_api, output_api,
Artem Titov739351d2018-05-11 12:21:36 +0200511 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200512 error_formatter=_ReportFileAndLine):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100513 """Make sure that no more dependencies on stringstream are added."""
514 error_msg = ('Usage of <sstream>, <istream> and <ostream> in WebRTC is '
515 'deprecated.\n'
516 'This includes the following types:\n'
517 'std::istringstream, std::ostringstream, std::wistringstream, '
518 'std::wostringstream,\n'
519 'std::wstringstream, std::ostream, std::wostream, std::istream,'
520 'std::wistream,\n'
521 'std::iostream, std::wiostream.\n'
522 'If you are not adding this code (e.g. you are just moving '
523 'existing code),\n'
524 'you can add a comment on the line that causes the problem:\n\n'
525 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
526 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
527 '\n'
Karl Wibergebd01e82018-03-14 15:08:39 +0100528 'If you are adding new code, consider using '
529 'rtc::SimpleStringBuilder\n'
530 '(in rtc_base/strings/string_builder.h).\n'
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100531 'Affected files:\n')
532 errors = [] # 2-element tuples with (file, line number)
533 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
534 usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
535 no_presubmit_re = input_api.re.compile(
Jonas Olsson74395342018-04-03 12:22:07 +0200536 r'// no-presubmit-check TODO\(webrtc:8982\)')
Artem Titova04d1402018-05-11 11:23:00 +0200537 file_filter = lambda x: (input_api.FilterSourceFile(x)
538 and source_file_filter(x))
Mirko Bonadei571791a2019-05-07 14:08:05 +0200539
540 def _IsException(file_path):
541 is_test = any(file_path.endswith(x) for x in ['_test.cc', '_tests.cc',
542 '_unittest.cc',
543 '_unittests.cc'])
Patrik Höglund2ea27962020-01-13 15:10:40 +0100544 return (file_path.startswith('examples') or
545 file_path.startswith('test') or
546 is_test)
547
Mirko Bonadei571791a2019-05-07 14:08:05 +0200548
Artem Titova04d1402018-05-11 11:23:00 +0200549 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei571791a2019-05-07 14:08:05 +0200550 # Usage of stringstream is allowed under examples/ and in tests.
551 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
Mirko Bonadeid2c83322018-03-19 10:31:47 +0000552 continue
553 for line_num, line in f.ChangedContents():
554 if ((include_re.search(line) or usage_re.search(line))
555 and not no_presubmit_re.search(line)):
556 errors.append(error_formatter(f.LocalPath(), line_num))
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100557 if errors:
558 return [output_api.PresubmitError(error_msg, errors)]
559 return []
560
Artem Titove92675b2018-05-22 10:21:27 +0200561
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200562def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
563 """Checks that public_deps is not used without a good reason."""
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100564 result = []
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200565 no_presubmit_check_re = input_api.re.compile(
Joe Chen0b3a6e32019-12-26 23:01:42 -0800566 r'# no-presubmit-check TODO\(webrtc:\d+\)')
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200567 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
568 'because it doesn\'t map well to downstream build systems.\n'
569 'Used in: %s (line %d).\n'
570 'If you are not adding this code (e.g. you are just moving '
Patrik Höglund81c7a602020-01-30 11:32:33 +0100571 'existing code) or you have a good reason, you can add this '
572 'comment (verbatim) on the line that causes the problem:\n\n'
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200573 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100574 for affected_file in gn_files:
575 for (line_number, affected_line) in affected_file.ChangedContents():
Patrik Höglund81c7a602020-01-30 11:32:33 +0100576 if 'public_deps' in affected_line:
577 surpressed = no_presubmit_check_re.search(affected_line)
578 if not surpressed:
579 result.append(
580 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
581 line_number)))
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100582 return result
583
Artem Titove92675b2018-05-22 10:21:27 +0200584
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700585def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Patrik Höglund6f491062018-01-11 12:04:23 +0100586 result = []
587 error_msg = ('check_includes overrides are not allowed since it can cause '
588 'incorrect dependencies to form. It effectively means that your '
589 'module can include any .h file without depending on its '
590 'corresponding target. There are some exceptional cases when '
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700591 'this is allowed: if so, get approval from a .gn owner in the '
Patrik Höglund6f491062018-01-11 12:04:23 +0100592 'root OWNERS file.\n'
593 'Used in: %s (line %d).')
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700594 no_presubmit_re = input_api.re.compile(
595 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
Patrik Höglund6f491062018-01-11 12:04:23 +0100596 for affected_file in gn_files:
597 for (line_number, affected_line) in affected_file.ChangedContents():
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700598 if ('check_includes' in affected_line
599 and not no_presubmit_re.search(affected_line)):
Patrik Höglund6f491062018-01-11 12:04:23 +0100600 result.append(
601 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
602 line_number)))
603 return result
604
Artem Titove92675b2018-05-22 10:21:27 +0200605
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200606def CheckGnChanges(input_api, output_api):
Artem Titova04d1402018-05-11 11:23:00 +0200607 file_filter = lambda x: (input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 15:56:08 +0200608 x, white_list=(r'.+\.(gn|gni)$',),
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200609 black_list=(r'.*/presubmit_checks_lib/testdata/.*',)))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700610
611 gn_files = []
Artem Titova04d1402018-05-11 11:23:00 +0200612 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200613 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700614
615 result = []
616 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200617 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200618 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
Mirko Bonadei2dcf3482020-06-05 14:30:41 +0200619 result.extend(CheckAbseilDependencies(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200620 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
621 output_api))
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200622 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700623 result.extend(CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200624 result.extend(CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
625 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700626 return result
627
Artem Titove92675b2018-05-22 10:21:27 +0200628
Oleh Prypin920b6532017-10-05 11:28:51 +0200629def CheckGnGen(input_api, output_api):
630 """Runs `gn gen --check` with default args to detect mismatches between
631 #includes and dependencies in the BUILD.gn files, as well as general build
632 errors.
633 """
634 with _AddToPath(input_api.os_path.join(
635 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
Yves Gerey546ee612019-02-26 17:04:16 +0100636 from build_helpers import RunGnCheck
Mirko Bonadeid8665442018-09-04 12:17:27 +0200637 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
Oleh Prypin920b6532017-10-05 11:28:51 +0200638 if errors:
639 return [output_api.PresubmitPromptWarning(
640 'Some #includes do not match the build dependency graph. Please run:\n'
641 ' gn gen --check <out_dir>',
642 long_text='\n\n'.join(errors))]
643 return []
644
Artem Titove92675b2018-05-22 10:21:27 +0200645
Artem Titova04d1402018-05-11 11:23:00 +0200646def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000647 """Runs checkdeps on #include statements added in this
648 change. Breaking - rules is an error, breaking ! rules is a
649 warning.
650 """
651 # Copied from Chromium's src/PRESUBMIT.py.
652
653 # We need to wait until we have an input_api object and use this
654 # roundabout construct to import checkdeps because this file is
655 # eval-ed and thus doesn't have __file__.
Mirko Bonadeid8665442018-09-04 12:17:27 +0200656 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
657 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
Oleh Prypin2f33a562017-10-04 20:17:54 +0200658 if not os.path.exists(checkdeps_path):
659 return [output_api.PresubmitError(
660 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
661 'download all the DEPS entries?' % checkdeps_path)]
662 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000663 import checkdeps
664 from cpp_checker import CppChecker
665 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000666
667 added_includes = []
Artem Titova04d1402018-05-11 11:23:00 +0200668 for f in input_api.AffectedFiles(file_filter=source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000669 if not CppChecker.IsCppFile(f.LocalPath()):
670 continue
671
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200672 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000673 added_includes.append([f.LocalPath(), changed_lines])
674
675 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
676
677 error_descriptions = []
678 warning_descriptions = []
679 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
680 added_includes):
681 description_with_path = '%s\n %s' % (path, rule_description)
682 if rule_type == Rule.DISALLOW:
683 error_descriptions.append(description_with_path)
684 else:
685 warning_descriptions.append(description_with_path)
686
687 results = []
688 if error_descriptions:
689 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700690 'You added one or more #includes that violate checkdeps rules.\n'
691 'Check that the DEPS files in these locations contain valid rules.\n'
692 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
693 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000694 error_descriptions))
695 if warning_descriptions:
696 results.append(output_api.PresubmitPromptOrNotify(
697 'You added one or more #includes of files that are temporarily\n'
698 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700699 '#include? See relevant DEPS file(s) for details and contacts.\n'
700 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
701 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000702 warning_descriptions))
703 return results
704
Artem Titove92675b2018-05-22 10:21:27 +0200705
charujain9893e252017-09-14 13:33:22 +0200706def CheckCommitMessageBugEntry(input_api, output_api):
707 """Check that bug entries are well-formed in commit message."""
708 bogus_bug_msg = (
Mirko Bonadei61880182017-10-12 15:12:35 +0200709 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
charujain9893e252017-09-14 13:33:22 +0200710 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
711 results = []
Mirko Bonadei61880182017-10-12 15:12:35 +0200712 for bug in input_api.change.BugsFromDescription():
charujain9893e252017-09-14 13:33:22 +0200713 bug = bug.strip()
714 if bug.lower() == 'none':
715 continue
charujain81a58c72017-09-25 13:25:45 +0200716 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200717 try:
718 if int(bug) > 100000:
719 # Rough indicator for current chromium bugs.
720 prefix_guess = 'chromium'
721 else:
722 prefix_guess = 'webrtc'
Mirko Bonadei61880182017-10-12 15:12:35 +0200723 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
charujain9893e252017-09-14 13:33:22 +0200724 (prefix_guess, bug))
725 except ValueError:
726 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200727 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200728 results.append(bogus_bug_msg % bug)
729 return [output_api.PresubmitError(r) for r in results]
730
Artem Titove92675b2018-05-22 10:21:27 +0200731
charujain9893e252017-09-14 13:33:22 +0200732def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei61880182017-10-12 15:12:35 +0200733 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700734
735 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200736 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700737 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200738
739 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
740 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700741 """
Mirko Bonadei61880182017-10-12 15:12:35 +0200742 if input_api.change.BugsFromDescription():
kjellanderd1e26a92016-09-19 08:11:16 -0700743 return []
744 else:
745 return [output_api.PresubmitError(
Mirko Bonadei61880182017-10-12 15:12:35 +0200746 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
kjellanderd1e26a92016-09-19 08:11:16 -0700747 'reference it using either of:\n'
Mirko Bonadei61880182017-10-12 15:12:35 +0200748 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
749 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000750
Artem Titove92675b2018-05-22 10:21:27 +0200751
Artem Titova04d1402018-05-11 11:23:00 +0200752def CheckJSONParseErrors(input_api, output_api, source_file_filter):
kjellander569cf942016-02-11 05:02:59 -0800753 """Check that JSON files do not contain syntax errors."""
754
755 def FilterFile(affected_file):
Artem Titova04d1402018-05-11 11:23:00 +0200756 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
757 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800758
759 def GetJSONParseError(input_api, filename):
760 try:
761 contents = input_api.ReadFile(filename)
762 input_api.json.loads(contents)
763 except ValueError as e:
764 return e
765 return None
766
767 results = []
768 for affected_file in input_api.AffectedFiles(
769 file_filter=FilterFile, include_deletes=False):
770 parse_error = GetJSONParseError(input_api,
771 affected_file.AbsoluteLocalPath())
772 if parse_error:
773 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
Artem Titove92675b2018-05-22 10:21:27 +0200774 (affected_file.LocalPath(),
775 parse_error)))
kjellander569cf942016-02-11 05:02:59 -0800776 return results
777
778
charujain9893e252017-09-14 13:33:22 +0200779def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700780 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200781 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
782
783 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200784 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200785 Join('rtc_tools', 'py_event_log_analyzer'),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200786 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800787 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200788 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800789 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200790 ]
791
792 tests = []
793 for directory in test_directories:
794 tests.extend(
795 input_api.canned_checks.GetUnitTestsInDirectory(
796 input_api,
797 output_api,
798 directory,
799 whitelist=[r'.+_test\.py$']))
800 return input_api.RunTests(tests, parallel=True)
801
802
Artem Titova04d1402018-05-11 11:23:00 +0200803def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
804 source_file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700805 """Checks that the namespace google::protobuf has not been used."""
806 files = []
807 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200808 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
Artem Titova04d1402018-05-11 11:23:00 +0200809 file_filter = lambda x: (input_api.FilterSourceFile(x)
810 and source_file_filter(x))
811 for f in input_api.AffectedSourceFiles(file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700812 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
813 continue
814 contents = input_api.ReadFile(f)
815 if pattern.search(contents):
816 files.append(f)
817
818 if files:
819 return [output_api.PresubmitError(
820 'Please avoid to use namespace `google::protobuf` directly.\n'
821 'Add a using directive in `%s` and include that header instead.'
822 % proto_utils_path, files)]
823 return []
824
825
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200826def _LicenseHeader(input_api):
827 """Returns the license header regexp."""
828 # Accept any year number from 2003 to the current year
829 current_year = int(input_api.time.strftime('%Y'))
830 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
831 years_re = '(' + '|'.join(allowed_years) + ')'
832 license_header = (
833 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
834 r'All [Rr]ights [Rr]eserved\.\n'
835 r'.*?\n'
836 r'.*? Use of this source code is governed by a BSD-style license\n'
837 r'.*? that can be found in the LICENSE file in the root of the source\n'
838 r'.*? tree\. An additional intellectual property rights grant can be '
839 r'found\n'
840 r'.*? in the file PATENTS\. All contributing project authors may\n'
841 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
842 ) % {
843 'year': years_re,
844 }
845 return license_header
846
847
charujain9893e252017-09-14 13:33:22 +0200848def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000849 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000850 results = []
tkchin42f580e2015-11-26 23:18:23 -0800851 # Filter out files that are in objc or ios dirs from being cpplint-ed since
852 # they do not follow C++ lint rules.
853 black_list = input_api.DEFAULT_BLACK_LIST + (
854 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000855 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800856 )
857 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200858 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800859 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200860 results.extend(input_api.canned_checks.CheckLicense(
861 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000862 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100863 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200864 r'^build[\\\/].*\.py$',
865 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700866 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100867 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200868 r'^out.*[\\\/].*\.py$',
869 r'^testing[\\\/].*\.py$',
870 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100871 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800872 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200873 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200874 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200875 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800876
nisse3d21e232016-09-02 03:07:06 -0700877 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200878 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
879 # we need to have different license checks in talk/ and webrtc/ directories.
880 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200881
tkchin3cd9a302016-06-08 12:40:28 -0700882 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
883 # ObjC subdirectories ObjC headers.
884 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100885 # Skip long-lines check for DEPS and GN files.
886 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
Artem Titova04d1402018-05-11 11:23:00 +0200887 # Also we will skip most checks for third_party directory.
Artem Titov42f0d782018-06-27 13:23:17 +0200888 third_party_filter_list = (r'^third_party[\\\/].+',)
tkchin3cd9a302016-06-08 12:40:28 -0700889 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
Artem Titova04d1402018-05-11 11:23:00 +0200890 black_list=build_file_filter_list + objc_filter_list +
891 third_party_filter_list)
tkchin3cd9a302016-06-08 12:40:28 -0700892 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
893 white_list=objc_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200894 non_third_party_sources = lambda x: input_api.FilterSourceFile(x,
895 black_list=third_party_filter_list)
896
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000897 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700898 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
899 results.extend(input_api.canned_checks.CheckLongLines(
900 input_api, output_api, maxlen=100,
901 source_file_filter=hundred_char_sources))
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000902 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
Artem Titova04d1402018-05-11 11:23:00 +0200903 input_api, output_api, source_file_filter=non_third_party_sources))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000904 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
Artem Titova04d1402018-05-11 11:23:00 +0200905 input_api, output_api, source_file_filter=non_third_party_sources))
kjellandere5dc62a2016-12-14 00:16:21 -0800906 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Oleh Prypine0735142018-10-04 11:15:54 +0200907 input_api, output_api, bot_whitelist=[
908 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com'
909 ]))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000910 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
Artem Titova04d1402018-05-11 11:23:00 +0200911 input_api, output_api, source_file_filter=non_third_party_sources))
Yves Gerey87a93532018-06-20 15:51:49 +0200912 results.extend(input_api.canned_checks.CheckPatchFormatted(
913 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200914 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200915 results.extend(CheckNoIOStreamInHeaders(
916 input_api, output_api, source_file_filter=non_third_party_sources))
917 results.extend(CheckNoPragmaOnce(
918 input_api, output_api, source_file_filter=non_third_party_sources))
919 results.extend(CheckNoFRIEND_TEST(
920 input_api, output_api, source_file_filter=non_third_party_sources))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200921 results.extend(CheckGnChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200922 results.extend(CheckUnwantedDependencies(
923 input_api, output_api, source_file_filter=non_third_party_sources))
924 results.extend(CheckJSONParseErrors(
925 input_api, output_api, source_file_filter=non_third_party_sources))
charujain9893e252017-09-14 13:33:22 +0200926 results.extend(RunPythonTests(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200927 results.extend(CheckUsageOfGoogleProtobufNamespace(
928 input_api, output_api, source_file_filter=non_third_party_sources))
929 results.extend(CheckOrphanHeaders(
930 input_api, output_api, source_file_filter=non_third_party_sources))
931 results.extend(CheckNewlineAtTheEndOfProtoFiles(
932 input_api, output_api, source_file_filter=non_third_party_sources))
933 results.extend(CheckNoStreamUsageIsAdded(
Artem Titov739351d2018-05-11 12:21:36 +0200934 input_api, output_api, non_third_party_sources))
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100935 results.extend(CheckNoTestCaseUsageIsAdded(
936 input_api, output_api, non_third_party_sources))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +0200937 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200938 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
tzika06bf852018-11-15 20:37:35 +0900939 results.extend(CheckAbslMemoryInclude(
940 input_api, output_api, non_third_party_sources))
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +0200941 results.extend(CheckBannedAbslMakeUnique(
942 input_api, output_api, non_third_party_sources))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200943 return results
944
945
946def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei90490372018-10-26 13:17:47 +0200947 """Check that 'include_rules' in api/DEPS is up to date.
948
949 The file api/DEPS must be kept up to date in order to avoid to avoid to
950 include internal header from WebRTC's api/ headers.
951
952 This check is focused on ensuring that 'include_rules' contains a deny
953 rule for each root level directory. More focused allow rules can be
954 added to 'specific_include_rules'.
955 """
Mirko Bonadeia418e672018-10-24 13:57:25 +0200956 results = []
957 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
958 with open(api_deps) as f:
959 deps_content = _ParseDeps(f.read())
960
961 include_rules = deps_content.get('include_rules', [])
Mirko Bonadei01e97ae2019-09-05 14:36:42 +0200962 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200963
Mirko Bonadei90490372018-10-26 13:17:47 +0200964 # Only check top level directories affected by the current CL.
965 dirs_to_check = set()
966 for f in input_api.AffectedFiles():
967 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
968 if len(path_tokens) > 1:
Mirko Bonadei01e97ae2019-09-05 14:36:42 +0200969 if (path_tokens[0] not in dirs_to_skip and
Mirko Bonadei90490372018-10-26 13:17:47 +0200970 os.path.isdir(os.path.join(input_api.PresubmitLocalPath(),
971 path_tokens[0]))):
972 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200973
Mirko Bonadei90490372018-10-26 13:17:47 +0200974 missing_include_rules = set()
975 for p in dirs_to_check:
Mirko Bonadeia418e672018-10-24 13:57:25 +0200976 rule = '-%s' % p
977 if rule not in include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200978 missing_include_rules.add(rule)
979
Mirko Bonadeia418e672018-10-24 13:57:25 +0200980 if missing_include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200981 error_msg = [
982 'include_rules = [\n',
983 ' ...\n',
984 ]
Mirko Bonadeia418e672018-10-24 13:57:25 +0200985
Mirko Bonadei90490372018-10-26 13:17:47 +0200986 for r in sorted(missing_include_rules):
987 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200988
Mirko Bonadei90490372018-10-26 13:17:47 +0200989 error_msg.append(' ...\n')
990 error_msg.append(']\n')
991
Mirko Bonadeia418e672018-10-24 13:57:25 +0200992 results.append(output_api.PresubmitError(
Mirko Bonadei90490372018-10-26 13:17:47 +0200993 'New root level directory detected! WebRTC api/ headers should '
994 'not #include headers from \n'
995 'the new directory, so please update "include_rules" in file\n'
996 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
997
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000998 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000999
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001000def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
1001 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
1002 and source_file_filter(f))
1003
1004 files = []
1005 for f in input_api.AffectedFiles(
1006 include_deletes=False, file_filter=file_filter):
1007 for _, line in f.ChangedContents():
1008 if 'absl::make_unique' in line:
1009 files.append(f)
1010 break
1011
1012 if len(files):
1013 return [output_api.PresubmitError(
1014 'Please use std::make_unique instead of absl::make_unique.\n'
1015 'Affected files:',
1016 files)]
1017 return []
1018
tzika06bf852018-11-15 20:37:35 +09001019def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
1020 pattern = input_api.re.compile(
1021 r'^#include\s*"absl/memory/memory.h"', input_api.re.MULTILINE)
1022 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
1023 and source_file_filter(f))
1024
1025 files = []
1026 for f in input_api.AffectedFiles(
1027 include_deletes=False, file_filter=file_filter):
1028 contents = input_api.ReadFile(f)
1029 if pattern.search(contents):
1030 continue
1031 for _, line in f.ChangedContents():
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001032 if 'absl::WrapUnique' in line:
tzika06bf852018-11-15 20:37:35 +09001033 files.append(f)
1034 break
1035
1036 if len(files):
1037 return [output_api.PresubmitError(
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001038 'Please include "absl/memory/memory.h" header for absl::WrapUnique.\n'
1039 'This header may or may not be included transitively depending on the '
1040 'C++ standard version.',
tzika06bf852018-11-15 20:37:35 +09001041 files)]
1042 return []
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001043
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001044def CheckChangeOnUpload(input_api, output_api):
1045 results = []
charujain9893e252017-09-14 13:33:22 +02001046 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 11:28:51 +02001047 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +02001048 results.extend(
1049 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +00001050 return results
1051
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001052
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001053def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +00001054 results = []
charujain9893e252017-09-14 13:33:22 +02001055 results.extend(CommonChecks(input_api, output_api))
1056 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
Artem Titov42f0d782018-06-27 13:23:17 +02001057 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001058 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
1059 input_api, output_api))
1060 results.extend(input_api.canned_checks.CheckChangeHasDescription(
1061 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +02001062 results.extend(CheckChangeHasBugField(input_api, output_api))
1063 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +00001064 results.extend(input_api.canned_checks.CheckTreeIsOpen(
1065 input_api, output_api,
1066 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +00001067 return results
mbonadei74973ed2017-05-09 07:58:05 -07001068
1069
Artem Titova04d1402018-05-11 11:23:00 +02001070def CheckOrphanHeaders(input_api, output_api, source_file_filter):
mbonadei74973ed2017-05-09 07:58:05 -07001071 # We need to wait until we have an input_api object and use this
1072 # roundabout construct to import prebubmit_checks_lib because this file is
1073 # eval-ed and thus doesn't have __file__.
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001074 error_msg = """{} should be listed in {}."""
mbonadei74973ed2017-05-09 07:58:05 -07001075 results = []
Patrik Höglund7e60de22018-01-09 14:22:00 +01001076 orphan_blacklist = [
1077 os.path.join('tools_webrtc', 'ios', 'SDK'),
1078 ]
Oleh Prypin2f33a562017-10-04 20:17:54 +02001079 with _AddToPath(input_api.os_path.join(
1080 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 07:58:05 -07001081 from check_orphan_headers import GetBuildGnPathFromFilePath
1082 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -07001083
Artem Titova04d1402018-05-11 11:23:00 +02001084 file_filter = lambda x: input_api.FilterSourceFile(
1085 x, black_list=orphan_blacklist) and source_file_filter(x)
1086 for f in input_api.AffectedSourceFiles(file_filter):
Patrik Höglund7e60de22018-01-09 14:22:00 +01001087 if f.LocalPath().endswith('.h'):
mbonadei74973ed2017-05-09 07:58:05 -07001088 file_path = os.path.abspath(f.LocalPath())
1089 root_dir = os.getcwd()
1090 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
1091 root_dir)
1092 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1093 if not in_build_gn:
1094 results.append(output_api.PresubmitError(error_msg.format(
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001095 f.LocalPath(), os.path.relpath(gn_file_path))))
mbonadei74973ed2017-05-09 07:58:05 -07001096 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001097
1098
Artem Titove92675b2018-05-22 10:21:27 +02001099def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001100 """Checks that all .proto files are terminated with a newline."""
1101 error_msg = 'File {} must end with exactly one newline.'
1102 results = []
Artem Titova04d1402018-05-11 11:23:00 +02001103 file_filter = lambda x: input_api.FilterSourceFile(
1104 x, white_list=(r'.+\.proto$',)) and source_file_filter(x)
1105 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001106 file_path = f.LocalPath()
1107 with open(file_path) as f:
1108 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +02001109 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001110 results.append(output_api.PresubmitError(error_msg.format(file_path)))
1111 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001112
1113
1114def _ExtractAddRulesFromParsedDeps(parsed_deps):
1115 """Extract the rules that add dependencies from a parsed DEPS file.
1116
1117 Args:
1118 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1119 add_rules = set()
1120 add_rules.update([
1121 rule[1:] for rule in parsed_deps.get('include_rules', [])
1122 if rule.startswith('+') or rule.startswith('!')
1123 ])
1124 for _, rules in parsed_deps.get('specific_include_rules',
1125 {}).iteritems():
1126 add_rules.update([
1127 rule[1:] for rule in rules
1128 if rule.startswith('+') or rule.startswith('!')
1129 ])
1130 return add_rules
1131
1132
1133def _ParseDeps(contents):
1134 """Simple helper for parsing DEPS files."""
1135 # Stubs for handling special syntax in the root DEPS file.
1136 class VarImpl(object):
1137
1138 def __init__(self, local_scope):
1139 self._local_scope = local_scope
1140
1141 def Lookup(self, var_name):
1142 """Implements the Var syntax."""
1143 try:
1144 return self._local_scope['vars'][var_name]
1145 except KeyError:
1146 raise Exception('Var is not defined: %s' % var_name)
1147
1148 local_scope = {}
1149 global_scope = {
1150 'Var': VarImpl(local_scope).Lookup,
1151 }
1152 exec contents in global_scope, local_scope
1153 return local_scope
1154
1155
1156def _CalculateAddedDeps(os_path, old_contents, new_contents):
1157 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
1158 a set of DEPS entries that we should look up.
1159
1160 For a directory (rather than a specific filename) we fake a path to
1161 a specific filename by adding /DEPS. This is chosen as a file that
1162 will seldom or never be subject to per-file include_rules.
1163 """
1164 # We ignore deps entries on auto-generated directories.
1165 auto_generated_dirs = ['grit', 'jni']
1166
1167 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1168 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1169
1170 added_deps = new_deps.difference(old_deps)
1171
1172 results = set()
1173 for added_dep in added_deps:
1174 if added_dep.split('/')[0] in auto_generated_dirs:
1175 continue
1176 # Assume that a rule that ends in .h is a rule for a specific file.
1177 if added_dep.endswith('.h'):
1178 results.add(added_dep)
1179 else:
1180 results.add(os_path.join(added_dep, 'DEPS'))
1181 return results
1182
1183
1184def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1185 """When a dependency prefixed with + is added to a DEPS file, we
1186 want to make sure that the change is reviewed by an OWNER of the
1187 target file or directory, to avoid layering violations from being
1188 introduced. This check verifies that this happens.
1189 """
1190 virtual_depended_on_files = set()
1191
1192 file_filter = lambda f: not input_api.re.match(
1193 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1194 for f in input_api.AffectedFiles(include_deletes=False,
1195 file_filter=file_filter):
1196 filename = input_api.os_path.basename(f.LocalPath())
1197 if filename == 'DEPS':
1198 virtual_depended_on_files.update(_CalculateAddedDeps(
1199 input_api.os_path,
1200 '\n'.join(f.OldContents()),
1201 '\n'.join(f.NewContents())))
1202
1203 if not virtual_depended_on_files:
1204 return []
1205
1206 if input_api.is_committing:
1207 if input_api.tbr:
1208 return [output_api.PresubmitNotifyResult(
1209 '--tbr was specified, skipping OWNERS check for DEPS additions')]
1210 if input_api.dry_run:
1211 return [output_api.PresubmitNotifyResult(
1212 'This is a dry run, skipping OWNERS check for DEPS additions')]
1213 if not input_api.change.issue:
1214 return [output_api.PresubmitError(
1215 "DEPS approval by OWNERS check failed: this change has "
1216 "no change number, so we can't check it for approvals.")]
1217 output = output_api.PresubmitError
1218 else:
1219 output = output_api.PresubmitNotifyResult
1220
1221 owners_db = input_api.owners_db
1222 owner_email, reviewers = (
1223 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1224 input_api,
1225 owners_db.email_regexp,
1226 approval_needed=input_api.is_committing))
1227
1228 owner_email = owner_email or input_api.change.author_email
1229
1230 reviewers_plus_owner = set(reviewers)
1231 if owner_email:
1232 reviewers_plus_owner.add(owner_email)
1233 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1234 reviewers_plus_owner)
1235
1236 # We strip the /DEPS part that was added by
1237 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1238 # directory.
1239 def StripDeps(path):
1240 start_deps = path.rfind('/DEPS')
1241 if start_deps != -1:
1242 return path[:start_deps]
1243 else:
1244 return path
1245 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1246 for path in missing_files]
1247
1248 if unapproved_dependencies:
1249 output_list = [
1250 output('You need LGTM from owners of depends-on paths in DEPS that were '
1251 'modified in this CL:\n %s' %
1252 '\n '.join(sorted(unapproved_dependencies)))]
1253 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1254 output_list.append(output(
1255 'Suggested missing target path OWNERS:\n %s' %
1256 '\n '.join(suggested_owners or [])))
1257 return output_list
1258
1259 return []