blob: 199b726f3eb8d85a9493a40ef840f03b21f19235 [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.
100TARGET_RE = re.compile(r'(?P<indent>\s*)\w+\("(?P<target_name>\w+)"\) {'
101 r'(?P<target_contents>.*?)'
102 r'(?P=indent)}',
103 re.MULTILINE | re.DOTALL)
104
105# SOURCES_RE matches a block of sources inside a GN target.
106SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]',
107 re.MULTILINE | re.DOTALL)
108
109# FILE_PATH_RE matchies a file path.
110FILE_PATH_RE = re.compile(r'"(?P<file_path>(\w|\/)+)(?P<extension>\.\w+)"')
111
kjellander53047c92015-12-02 23:56:14 -0800112
Mirko Bonadeid8665442018-09-04 12:17:27 +0200113def FindSrcDirPath(starting_dir):
114 """Returns the abs path to the src/ dir of the project."""
115 src_dir = starting_dir
116 while os.path.basename(src_dir) != 'src':
117 src_dir = os.path.normpath(os.path.join(src_dir, os.pardir))
118 return src_dir
119
120
Oleh Prypin2f33a562017-10-04 20:17:54 +0200121@contextmanager
122def _AddToPath(*paths):
123 original_sys_path = sys.path
124 sys.path.extend(paths)
125 try:
126 yield
127 finally:
128 # Restore sys.path to what it was before.
129 sys.path = original_sys_path
ehmaldonado4fb97462017-01-30 05:27:22 -0800130
131
charujain9893e252017-09-14 13:33:22 +0200132def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800133 """Ensures the list of native API header directories is up to date."""
134 non_existing_paths = []
135 native_api_full_paths = [
136 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700137 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800138 for path in native_api_full_paths:
139 if not os.path.isdir(path):
140 non_existing_paths.append(path)
141 if non_existing_paths:
142 return [output_api.PresubmitError(
143 'Directories to native API headers have changed which has made the '
144 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
145 'location of our native APIs.',
146 non_existing_paths)]
147 return []
148
Artem Titove92675b2018-05-22 10:21:27 +0200149
kjellanderc88b5d52017-04-05 06:42:43 -0700150API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700151You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800152 1. Make compatible changes that don't break existing clients. Usually
153 this is done by keeping the existing method signatures unchanged.
154 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700155 3. Create a timeline and plan for when the deprecated stuff will be
156 removed. (The amount of time we give users to change their code
157 should be informed by how much work it is for them. If they just
158 need to replace one name with another or something equally
159 simple, 1-2 weeks might be good; if they need to do serious work,
160 up to 3 months may be called for.)
161 4. Update/inform existing downstream code owners to stop using the
162 deprecated stuff. (Send announcements to
163 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
164 5. Remove the deprecated stuff, once the agreed-upon amount of time
165 has passed.
166Related files:
167"""
kjellander53047c92015-12-02 23:56:14 -0800168
Artem Titove92675b2018-05-22 10:21:27 +0200169
charujain9893e252017-09-14 13:33:22 +0200170def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800171 """Checks to remind proper changing of native APIs."""
172 files = []
Karl Wiberg6bfac032017-10-27 15:14:20 +0200173 source_file_filter = lambda x: input_api.FilterSourceFile(
174 x, white_list=[r'.+\.(gn|gni|h)$'])
175 for f in input_api.AffectedSourceFiles(source_file_filter):
176 for path in API_DIRS:
177 dn = os.path.dirname(f.LocalPath())
178 if path == 'api':
179 # Special case: Subdirectories included.
180 if dn == 'api' or dn.startswith('api/'):
Niels Möller1d201852019-06-26 12:58:27 +0200181 files.append(f.LocalPath())
Karl Wiberg6bfac032017-10-27 15:14:20 +0200182 else:
183 # Normal case: Subdirectories not included.
184 if dn == path:
Niels Möller1d201852019-06-26 12:58:27 +0200185 files.append(f.LocalPath())
kjellander53047c92015-12-02 23:56:14 -0800186
187 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700188 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800189 return []
190
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100191
Artem Titova04d1402018-05-11 11:23:00 +0200192def CheckNoIOStreamInHeaders(input_api, output_api,
193 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000194 """Checks to make sure no .h files include <iostream>."""
195 files = []
196 pattern = input_api.re.compile(r'^#include\s*<iostream>',
197 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200198 file_filter = lambda x: (input_api.FilterSourceFile(x)
199 and source_file_filter(x))
200 for f in input_api.AffectedSourceFiles(file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000201 if not f.LocalPath().endswith('.h'):
202 continue
203 contents = input_api.ReadFile(f)
204 if pattern.search(contents):
205 files.append(f)
206
207 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200208 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000209 'Do not #include <iostream> in header files, since it inserts static ' +
210 'initialization into every file including the header. Instead, ' +
211 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200212 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000213 return []
214
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000215
Artem Titova04d1402018-05-11 11:23:00 +0200216def CheckNoPragmaOnce(input_api, output_api,
217 source_file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800218 """Make sure that banned functions are not used."""
219 files = []
220 pattern = input_api.re.compile(r'^#pragma\s+once',
221 input_api.re.MULTILINE)
Artem Titova04d1402018-05-11 11:23:00 +0200222 file_filter = lambda x: (input_api.FilterSourceFile(x)
223 and source_file_filter(x))
224 for f in input_api.AffectedSourceFiles(file_filter):
kjellander6aeef742017-02-20 01:13:18 -0800225 if not f.LocalPath().endswith('.h'):
226 continue
227 contents = input_api.ReadFile(f)
228 if pattern.search(contents):
229 files.append(f)
230
231 if files:
232 return [output_api.PresubmitError(
233 'Do not use #pragma once in header files.\n'
234 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
235 files)]
236 return []
237
238
Artem Titova04d1402018-05-11 11:23:00 +0200239def CheckNoFRIEND_TEST(input_api, output_api, # pylint: disable=invalid-name
240 source_file_filter):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000241 """Make sure that gtest's FRIEND_TEST() macro is not used, the
242 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
243 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
244 problems = []
245
Artem Titova04d1402018-05-11 11:23:00 +0200246 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
247 and source_file_filter(f))
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000248 for f in input_api.AffectedFiles(file_filter=file_filter):
249 for line_num, line in f.ChangedContents():
250 if 'FRIEND_TEST(' in line:
251 problems.append(' %s:%d' % (f.LocalPath(), line_num))
252
253 if not problems:
254 return []
255 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
256 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
257 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
258
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000259
charujain9893e252017-09-14 13:33:22 +0200260def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700261 """ Checks if a file is blacklisted for lint check."""
262 for path in blacklist_paths:
263 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100264 return True
265 return False
266
267
charujain9893e252017-09-14 13:33:22 +0200268def CheckApprovedFilesLintClean(input_api, output_api,
Artem Titova04d1402018-05-11 11:23:00 +0200269 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700270 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200271 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000272 depot_tools/presubmit_canned_checks.py but has less filters and only checks
273 added files."""
274 result = []
275
276 # Initialize cpplint.
277 import cpplint
278 # Access to a protected member _XX of a client class
279 # pylint: disable=W0212
280 cpplint._cpplint_state.ResetErrorCounts()
281
jbauchc4e3ead2016-02-19 00:25:55 -0800282 lint_filters = cpplint._Filters()
283 lint_filters.extend(BLACKLIST_LINT_FILTERS)
284 cpplint._SetFilters(','.join(lint_filters))
285
oprypin2aa463f2017-03-23 03:17:02 -0700286 # Create a platform independent blacklist for cpplint.
287 blacklist_paths = [input_api.os_path.join(*path.split('/'))
288 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100289
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000290 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700291 # default when running cpplint.py from command line. To make it possible to
292 # work with not-yet-converted code, we're only applying it to new (or
293 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000294 verbosity_level = 1
295 files = []
296 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200297 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200298 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
Artem Titove92675b2018-05-22 10:21:27 +0200299 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000300 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000301
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000302 for file_name in files:
303 cpplint.ProcessFile(file_name, verbosity_level)
304
305 if cpplint._cpplint_state.error_count > 0:
306 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700307 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000308 else:
309 res_type = output_api.PresubmitPromptWarning
310 result = [res_type('Changelist failed cpplint.py check.')]
311
312 return result
313
Artem Titove92675b2018-05-22 10:21:27 +0200314
charujain9893e252017-09-14 13:33:22 +0200315def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700316 # Disallow referencing source files with paths above the GN file location.
317 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
318 re.MULTILINE | re.DOTALL)
319 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
320 violating_gn_files = set()
321 violating_source_entries = []
322 for gn_file in gn_files:
323 contents = input_api.ReadFile(gn_file)
324 for source_block_match in source_pattern.finditer(contents):
325 # Find all source list entries starting with ../ in the source block
326 # (exclude overrides entries).
327 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
328 source_file = file_list_match.group(1)
329 if 'overrides/' not in source_file:
330 violating_source_entries.append(source_file)
331 violating_gn_files.add(gn_file)
332 if violating_gn_files:
333 return [output_api.PresubmitError(
334 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100335 'allowed. Please introduce new GN targets in the proper location '
336 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700337 'Invalid source entries:\n'
338 '%s\n'
339 'Violating GN files:' % '\n'.join(violating_source_entries),
340 items=violating_gn_files)]
341 return []
342
Artem Titove92675b2018-05-22 10:21:27 +0200343
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200344def CheckNoMixingSources(input_api, gn_files, output_api):
345 """Disallow mixing C, C++ and Obj-C/Obj-C++ in the same target.
346
347 See bugs.webrtc.org/7743 for more context.
348 """
Artem Titove92675b2018-05-22 10:21:27 +0200349
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200350 def _MoreThanOneSourceUsed(*sources_lists):
351 sources_used = 0
352 for source_list in sources_lists:
353 if len(source_list):
354 sources_used += 1
355 return sources_used > 1
356
357 errors = defaultdict(lambda: [])
kjellander7439f972016-12-05 22:47:46 -0800358 for gn_file in gn_files:
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200359 gn_file_content = input_api.ReadFile(gn_file)
360 for target_match in TARGET_RE.finditer(gn_file_content):
361 # list_of_sources is a list of tuples of the form
362 # (c_files, cc_files, objc_files) that keeps track of all the sources
363 # defined in a target. A GN target can have more that on definition of
364 # sources (since it supports if/else statements).
365 # E.g.:
366 # rtc_static_library("foo") {
367 # if (is_win) {
368 # sources = [ "foo.cc" ]
369 # } else {
370 # sources = [ "foo.mm" ]
371 # }
372 # }
373 # This is allowed and the presubmit check should support this case.
374 list_of_sources = []
kjellander7439f972016-12-05 22:47:46 -0800375 c_files = []
376 cc_files = []
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200377 objc_files = []
378 target_name = target_match.group('target_name')
379 target_contents = target_match.group('target_contents')
380 for sources_match in SOURCES_RE.finditer(target_contents):
381 if '+=' not in sources_match.group(0):
382 if c_files or cc_files or objc_files:
383 list_of_sources.append((c_files, cc_files, objc_files))
384 c_files = []
385 cc_files = []
386 objc_files = []
387 for file_match in FILE_PATH_RE.finditer(sources_match.group(1)):
388 file_path = file_match.group('file_path')
389 extension = file_match.group('extension')
390 if extension == '.c':
391 c_files.append(file_path + extension)
392 if extension == '.cc':
393 cc_files.append(file_path + extension)
394 if extension in ['.m', '.mm']:
395 objc_files.append(file_path + extension)
396 list_of_sources.append((c_files, cc_files, objc_files))
397 for c_files_list, cc_files_list, objc_files_list in list_of_sources:
398 if _MoreThanOneSourceUsed(c_files_list, cc_files_list, objc_files_list):
399 all_sources = sorted(c_files_list + cc_files_list + objc_files_list)
400 errors[gn_file.LocalPath()].append((target_name, all_sources))
401 if errors:
kjellander7439f972016-12-05 22:47:46 -0800402 return [output_api.PresubmitError(
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200403 'GN targets cannot mix .c, .cc and .m (or .mm) source files.\n'
404 'Please create a separate target for each collection of sources.\n'
kjellander7439f972016-12-05 22:47:46 -0800405 'Mixed sources: \n'
406 '%s\n'
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200407 'Violating GN files:\n%s\n' % (json.dumps(errors, indent=2),
408 '\n'.join(errors.keys())))]
kjellander7439f972016-12-05 22:47:46 -0800409 return []
410
Artem Titove92675b2018-05-22 10:21:27 +0200411
charujain9893e252017-09-14 13:33:22 +0200412def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800413 cwd = input_api.PresubmitLocalPath()
Oleh Prypin2f33a562017-10-04 20:17:54 +0200414 with _AddToPath(input_api.os_path.join(
415 cwd, 'tools_webrtc', 'presubmit_checks_lib')):
416 from check_package_boundaries import CheckPackageBoundaries
417 build_files = [os.path.join(cwd, gn_file.LocalPath()) for gn_file in gn_files]
418 errors = CheckPackageBoundaries(cwd, build_files)[:5]
419 if errors:
ehmaldonado4fb97462017-01-30 05:27:22 -0800420 return [output_api.PresubmitError(
Oleh Prypin2f33a562017-10-04 20:17:54 +0200421 'There are package boundary violations in the following GN files:',
422 long_text='\n\n'.join(str(err) for err in errors))]
ehmaldonado4fb97462017-01-30 05:27:22 -0800423 return []
424
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100425
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200426def _ReportFileAndLine(filename, line_num):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100427 """Default error formatter for _FindNewViolationsOfRule."""
428 return '%s (line %s)' % (filename, line_num)
429
430
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200431def CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api, output_api,
432 error_formatter=_ReportFileAndLine):
433 """Make sure that warning suppression flags are not added wihtout a reason."""
434 msg = ('Usage of //build/config/clang:extra_warnings is discouraged '
435 'in WebRTC.\n'
436 'If you are not adding this code (e.g. you are just moving '
437 'existing code) or you want to add an exception,\n'
438 'you can add a comment on the line that causes the problem:\n\n'
439 '"-Wno-odr" # no-presubmit-check TODO(bugs.webrtc.org/BUG_ID)\n'
440 '\n'
441 'Affected files:\n')
442 errors = [] # 2-element tuples with (file, line number)
443 clang_warn_re = input_api.re.compile(r'//build/config/clang:extra_warnings')
444 no_presubmit_re = input_api.re.compile(
445 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
446 for f in gn_files:
447 for line_num, line in f.ChangedContents():
448 if clang_warn_re.search(line) and not no_presubmit_re.search(line):
449 errors.append(error_formatter(f.LocalPath(), line_num))
450 if errors:
451 return [output_api.PresubmitError(msg, errors)]
452 return []
453
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100454
455def CheckNoTestCaseUsageIsAdded(input_api, output_api, source_file_filter,
456 error_formatter=_ReportFileAndLine):
457 error_msg = ('Usage of legacy GoogleTest API detected!\nPlease use the '
458 'new API: https://github.com/google/googletest/blob/master/'
459 'googletest/docs/primer.md#beware-of-the-nomenclature.\n'
460 'Affected files:\n')
461 errors = [] # 2-element tuples with (file, line number)
462 test_case_re = input_api.re.compile(r'TEST_CASE')
463 file_filter = lambda f: (source_file_filter(f)
464 and f.LocalPath().endswith('.cc'))
465 for f in input_api.AffectedSourceFiles(file_filter):
466 for line_num, line in f.ChangedContents():
467 if test_case_re.search(line):
468 errors.append(error_formatter(f.LocalPath(), line_num))
469 if errors:
470 return [output_api.PresubmitError(error_msg, errors)]
471 return []
472
473
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100474def CheckNoStreamUsageIsAdded(input_api, output_api,
Artem Titov739351d2018-05-11 12:21:36 +0200475 source_file_filter,
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200476 error_formatter=_ReportFileAndLine):
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100477 """Make sure that no more dependencies on stringstream are added."""
478 error_msg = ('Usage of <sstream>, <istream> and <ostream> in WebRTC is '
479 'deprecated.\n'
480 'This includes the following types:\n'
481 'std::istringstream, std::ostringstream, std::wistringstream, '
482 'std::wostringstream,\n'
483 'std::wstringstream, std::ostream, std::wostream, std::istream,'
484 'std::wistream,\n'
485 'std::iostream, std::wiostream.\n'
486 'If you are not adding this code (e.g. you are just moving '
487 'existing code),\n'
488 'you can add a comment on the line that causes the problem:\n\n'
489 '#include <sstream> // no-presubmit-check TODO(webrtc:8982)\n'
490 'std::ostream& F() { // no-presubmit-check TODO(webrtc:8982)\n'
491 '\n'
Karl Wibergebd01e82018-03-14 15:08:39 +0100492 'If you are adding new code, consider using '
493 'rtc::SimpleStringBuilder\n'
494 '(in rtc_base/strings/string_builder.h).\n'
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100495 'Affected files:\n')
496 errors = [] # 2-element tuples with (file, line number)
497 include_re = input_api.re.compile(r'#include <(i|o|s)stream>')
498 usage_re = input_api.re.compile(r'std::(w|i|o|io|wi|wo|wio)(string)*stream')
499 no_presubmit_re = input_api.re.compile(
Jonas Olsson74395342018-04-03 12:22:07 +0200500 r'// no-presubmit-check TODO\(webrtc:8982\)')
Artem Titova04d1402018-05-11 11:23:00 +0200501 file_filter = lambda x: (input_api.FilterSourceFile(x)
502 and source_file_filter(x))
Mirko Bonadei571791a2019-05-07 14:08:05 +0200503
504 def _IsException(file_path):
505 is_test = any(file_path.endswith(x) for x in ['_test.cc', '_tests.cc',
506 '_unittest.cc',
507 '_unittests.cc'])
Patrik Höglund2ea27962020-01-13 15:10:40 +0100508 return (file_path.startswith('examples') or
509 file_path.startswith('test') or
510 is_test)
511
Mirko Bonadei571791a2019-05-07 14:08:05 +0200512
Artem Titova04d1402018-05-11 11:23:00 +0200513 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei571791a2019-05-07 14:08:05 +0200514 # Usage of stringstream is allowed under examples/ and in tests.
515 if f.LocalPath() == 'PRESUBMIT.py' or _IsException(f.LocalPath()):
Mirko Bonadeid2c83322018-03-19 10:31:47 +0000516 continue
517 for line_num, line in f.ChangedContents():
518 if ((include_re.search(line) or usage_re.search(line))
519 and not no_presubmit_re.search(line)):
520 errors.append(error_formatter(f.LocalPath(), line_num))
Mirko Bonadeia51bbd82018-03-08 16:15:45 +0100521 if errors:
522 return [output_api.PresubmitError(error_msg, errors)]
523 return []
524
Artem Titove92675b2018-05-22 10:21:27 +0200525
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200526def CheckPublicDepsIsNotUsed(gn_files, input_api, output_api):
527 """Checks that public_deps is not used without a good reason."""
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100528 result = []
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200529 no_presubmit_check_re = input_api.re.compile(
Joe Chen0b3a6e32019-12-26 23:01:42 -0800530 r'# no-presubmit-check TODO\(webrtc:\d+\)')
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200531 error_msg = ('public_deps is not recommended in WebRTC BUILD.gn files '
532 'because it doesn\'t map well to downstream build systems.\n'
533 'Used in: %s (line %d).\n'
534 'If you are not adding this code (e.g. you are just moving '
Patrik Höglund81c7a602020-01-30 11:32:33 +0100535 'existing code) or you have a good reason, you can add this '
536 'comment (verbatim) on the line that causes the problem:\n\n'
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200537 'public_deps = [ # no-presubmit-check TODO(webrtc:8603)\n')
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100538 for affected_file in gn_files:
539 for (line_number, affected_line) in affected_file.ChangedContents():
Patrik Höglund81c7a602020-01-30 11:32:33 +0100540 if 'public_deps' in affected_line:
541 surpressed = no_presubmit_check_re.search(affected_line)
542 if not surpressed:
543 result.append(
544 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
545 line_number)))
Mirko Bonadei5c1ad592017-12-12 11:52:27 +0100546 return result
547
Artem Titove92675b2018-05-22 10:21:27 +0200548
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700549def CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api):
Patrik Höglund6f491062018-01-11 12:04:23 +0100550 result = []
551 error_msg = ('check_includes overrides are not allowed since it can cause '
552 'incorrect dependencies to form. It effectively means that your '
553 'module can include any .h file without depending on its '
554 'corresponding target. There are some exceptional cases when '
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700555 'this is allowed: if so, get approval from a .gn owner in the '
Patrik Höglund6f491062018-01-11 12:04:23 +0100556 'root OWNERS file.\n'
557 'Used in: %s (line %d).')
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700558 no_presubmit_re = input_api.re.compile(
559 r'# no-presubmit-check TODO\(bugs\.webrtc\.org/\d+\)')
Patrik Höglund6f491062018-01-11 12:04:23 +0100560 for affected_file in gn_files:
561 for (line_number, affected_line) in affected_file.ChangedContents():
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700562 if ('check_includes' in affected_line
563 and not no_presubmit_re.search(affected_line)):
Patrik Höglund6f491062018-01-11 12:04:23 +0100564 result.append(
565 output_api.PresubmitError(error_msg % (affected_file.LocalPath(),
566 line_number)))
567 return result
568
Artem Titove92675b2018-05-22 10:21:27 +0200569
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200570def CheckGnChanges(input_api, output_api):
Artem Titova04d1402018-05-11 11:23:00 +0200571 file_filter = lambda x: (input_api.FilterSourceFile(
Oleh Prypinafe01652017-10-04 15:56:08 +0200572 x, white_list=(r'.+\.(gn|gni)$',),
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200573 black_list=(r'.*/presubmit_checks_lib/testdata/.*',)))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700574
575 gn_files = []
Artem Titova04d1402018-05-11 11:23:00 +0200576 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200577 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700578
579 result = []
580 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200581 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
Mirko Bonadei4dc4e252017-09-19 13:49:16 +0200582 result.extend(CheckNoMixingSources(input_api, gn_files, output_api))
583 result.extend(CheckNoPackageBoundaryViolations(input_api, gn_files,
584 output_api))
Mirko Bonadeia05d47e2018-05-09 11:03:38 +0200585 result.extend(CheckPublicDepsIsNotUsed(gn_files, input_api, output_api))
Mirko Bonadei05691dd2019-10-22 07:34:24 -0700586 result.extend(CheckCheckIncludesIsNotUsed(gn_files, input_api, output_api))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200587 result.extend(CheckNoWarningSuppressionFlagsAreAdded(gn_files, input_api,
588 output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700589 return result
590
Artem Titove92675b2018-05-22 10:21:27 +0200591
Oleh Prypin920b6532017-10-05 11:28:51 +0200592def CheckGnGen(input_api, output_api):
593 """Runs `gn gen --check` with default args to detect mismatches between
594 #includes and dependencies in the BUILD.gn files, as well as general build
595 errors.
596 """
597 with _AddToPath(input_api.os_path.join(
598 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
Yves Gerey546ee612019-02-26 17:04:16 +0100599 from build_helpers import RunGnCheck
Mirko Bonadeid8665442018-09-04 12:17:27 +0200600 errors = RunGnCheck(FindSrcDirPath(input_api.PresubmitLocalPath()))[:5]
Oleh Prypin920b6532017-10-05 11:28:51 +0200601 if errors:
602 return [output_api.PresubmitPromptWarning(
603 'Some #includes do not match the build dependency graph. Please run:\n'
604 ' gn gen --check <out_dir>',
605 long_text='\n\n'.join(errors))]
606 return []
607
Artem Titove92675b2018-05-22 10:21:27 +0200608
Artem Titova04d1402018-05-11 11:23:00 +0200609def CheckUnwantedDependencies(input_api, output_api, source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000610 """Runs checkdeps on #include statements added in this
611 change. Breaking - rules is an error, breaking ! rules is a
612 warning.
613 """
614 # Copied from Chromium's src/PRESUBMIT.py.
615
616 # We need to wait until we have an input_api object and use this
617 # roundabout construct to import checkdeps because this file is
618 # eval-ed and thus doesn't have __file__.
Mirko Bonadeid8665442018-09-04 12:17:27 +0200619 src_path = FindSrcDirPath(input_api.PresubmitLocalPath())
620 checkdeps_path = input_api.os_path.join(src_path, 'buildtools', 'checkdeps')
Oleh Prypin2f33a562017-10-04 20:17:54 +0200621 if not os.path.exists(checkdeps_path):
622 return [output_api.PresubmitError(
623 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
624 'download all the DEPS entries?' % checkdeps_path)]
625 with _AddToPath(checkdeps_path):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000626 import checkdeps
627 from cpp_checker import CppChecker
628 from rules import Rule
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000629
630 added_includes = []
Artem Titova04d1402018-05-11 11:23:00 +0200631 for f in input_api.AffectedFiles(file_filter=source_file_filter):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000632 if not CppChecker.IsCppFile(f.LocalPath()):
633 continue
634
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200635 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000636 added_includes.append([f.LocalPath(), changed_lines])
637
638 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
639
640 error_descriptions = []
641 warning_descriptions = []
642 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
643 added_includes):
644 description_with_path = '%s\n %s' % (path, rule_description)
645 if rule_type == Rule.DISALLOW:
646 error_descriptions.append(description_with_path)
647 else:
648 warning_descriptions.append(description_with_path)
649
650 results = []
651 if error_descriptions:
652 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700653 'You added one or more #includes that violate checkdeps rules.\n'
654 'Check that the DEPS files in these locations contain valid rules.\n'
655 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
656 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000657 error_descriptions))
658 if warning_descriptions:
659 results.append(output_api.PresubmitPromptOrNotify(
660 'You added one or more #includes of files that are temporarily\n'
661 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700662 '#include? See relevant DEPS file(s) for details and contacts.\n'
663 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
664 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000665 warning_descriptions))
666 return results
667
Artem Titove92675b2018-05-22 10:21:27 +0200668
charujain9893e252017-09-14 13:33:22 +0200669def CheckCommitMessageBugEntry(input_api, output_api):
670 """Check that bug entries are well-formed in commit message."""
671 bogus_bug_msg = (
Mirko Bonadei61880182017-10-12 15:12:35 +0200672 'Bogus Bug entry: %s. Please specify the issue tracker prefix and the '
charujain9893e252017-09-14 13:33:22 +0200673 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
674 results = []
Mirko Bonadei61880182017-10-12 15:12:35 +0200675 for bug in input_api.change.BugsFromDescription():
charujain9893e252017-09-14 13:33:22 +0200676 bug = bug.strip()
677 if bug.lower() == 'none':
678 continue
charujain81a58c72017-09-25 13:25:45 +0200679 if 'b/' not in bug and ':' not in bug:
charujain9893e252017-09-14 13:33:22 +0200680 try:
681 if int(bug) > 100000:
682 # Rough indicator for current chromium bugs.
683 prefix_guess = 'chromium'
684 else:
685 prefix_guess = 'webrtc'
Mirko Bonadei61880182017-10-12 15:12:35 +0200686 results.append('Bug entry requires issue tracker prefix, e.g. %s:%s' %
charujain9893e252017-09-14 13:33:22 +0200687 (prefix_guess, bug))
688 except ValueError:
689 results.append(bogus_bug_msg % bug)
charujain81a58c72017-09-25 13:25:45 +0200690 elif not (re.match(r'\w+:\d+', bug) or re.match(r'b/\d+', bug)):
charujain9893e252017-09-14 13:33:22 +0200691 results.append(bogus_bug_msg % bug)
692 return [output_api.PresubmitError(r) for r in results]
693
Artem Titove92675b2018-05-22 10:21:27 +0200694
charujain9893e252017-09-14 13:33:22 +0200695def CheckChangeHasBugField(input_api, output_api):
Mirko Bonadei61880182017-10-12 15:12:35 +0200696 """Requires that the changelist is associated with a bug.
kjellanderd1e26a92016-09-19 08:11:16 -0700697
698 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
Mirko Bonadei61880182017-10-12 15:12:35 +0200699 since it fails the presubmit if the bug field is missing or doesn't contain
kjellanderd1e26a92016-09-19 08:11:16 -0700700 a bug reference.
Mirko Bonadei61880182017-10-12 15:12:35 +0200701
702 This supports both 'BUG=' and 'Bug:' since we are in the process of migrating
703 to Gerrit and it encourages the usage of 'Bug:'.
kjellanderd1e26a92016-09-19 08:11:16 -0700704 """
Mirko Bonadei61880182017-10-12 15:12:35 +0200705 if input_api.change.BugsFromDescription():
kjellanderd1e26a92016-09-19 08:11:16 -0700706 return []
707 else:
708 return [output_api.PresubmitError(
Mirko Bonadei61880182017-10-12 15:12:35 +0200709 'The "Bug: [bug number]" footer is mandatory. Please create a bug and '
kjellanderd1e26a92016-09-19 08:11:16 -0700710 'reference it using either of:\n'
Mirko Bonadei61880182017-10-12 15:12:35 +0200711 ' * https://bugs.webrtc.org - reference it using Bug: webrtc:XXXX\n'
712 ' * https://crbug.com - reference it using Bug: chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000713
Artem Titove92675b2018-05-22 10:21:27 +0200714
Artem Titova04d1402018-05-11 11:23:00 +0200715def CheckJSONParseErrors(input_api, output_api, source_file_filter):
kjellander569cf942016-02-11 05:02:59 -0800716 """Check that JSON files do not contain syntax errors."""
717
718 def FilterFile(affected_file):
Artem Titova04d1402018-05-11 11:23:00 +0200719 return (input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
720 and source_file_filter(affected_file))
kjellander569cf942016-02-11 05:02:59 -0800721
722 def GetJSONParseError(input_api, filename):
723 try:
724 contents = input_api.ReadFile(filename)
725 input_api.json.loads(contents)
726 except ValueError as e:
727 return e
728 return None
729
730 results = []
731 for affected_file in input_api.AffectedFiles(
732 file_filter=FilterFile, include_deletes=False):
733 parse_error = GetJSONParseError(input_api,
734 affected_file.AbsoluteLocalPath())
735 if parse_error:
736 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
Artem Titove92675b2018-05-22 10:21:27 +0200737 (affected_file.LocalPath(),
738 parse_error)))
kjellander569cf942016-02-11 05:02:59 -0800739 return results
740
741
charujain9893e252017-09-14 13:33:22 +0200742def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700743 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200744 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
745
746 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200747 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200748 Join('rtc_tools', 'py_event_log_analyzer'),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200749 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800750 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200751 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800752 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200753 ]
754
755 tests = []
756 for directory in test_directories:
757 tests.extend(
758 input_api.canned_checks.GetUnitTestsInDirectory(
759 input_api,
760 output_api,
761 directory,
762 whitelist=[r'.+_test\.py$']))
763 return input_api.RunTests(tests, parallel=True)
764
765
Artem Titova04d1402018-05-11 11:23:00 +0200766def CheckUsageOfGoogleProtobufNamespace(input_api, output_api,
767 source_file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700768 """Checks that the namespace google::protobuf has not been used."""
769 files = []
770 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200771 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
Artem Titova04d1402018-05-11 11:23:00 +0200772 file_filter = lambda x: (input_api.FilterSourceFile(x)
773 and source_file_filter(x))
774 for f in input_api.AffectedSourceFiles(file_filter):
mbonadei38415b22017-04-07 05:38:01 -0700775 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
776 continue
777 contents = input_api.ReadFile(f)
778 if pattern.search(contents):
779 files.append(f)
780
781 if files:
782 return [output_api.PresubmitError(
783 'Please avoid to use namespace `google::protobuf` directly.\n'
784 'Add a using directive in `%s` and include that header instead.'
785 % proto_utils_path, files)]
786 return []
787
788
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200789def _LicenseHeader(input_api):
790 """Returns the license header regexp."""
791 # Accept any year number from 2003 to the current year
792 current_year = int(input_api.time.strftime('%Y'))
793 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
794 years_re = '(' + '|'.join(allowed_years) + ')'
795 license_header = (
796 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
797 r'All [Rr]ights [Rr]eserved\.\n'
798 r'.*?\n'
799 r'.*? Use of this source code is governed by a BSD-style license\n'
800 r'.*? that can be found in the LICENSE file in the root of the source\n'
801 r'.*? tree\. An additional intellectual property rights grant can be '
802 r'found\n'
803 r'.*? in the file PATENTS\. All contributing project authors may\n'
804 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
805 ) % {
806 'year': years_re,
807 }
808 return license_header
809
810
charujain9893e252017-09-14 13:33:22 +0200811def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000812 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000813 results = []
tkchin42f580e2015-11-26 23:18:23 -0800814 # Filter out files that are in objc or ios dirs from being cpplint-ed since
815 # they do not follow C++ lint rules.
816 black_list = input_api.DEFAULT_BLACK_LIST + (
817 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000818 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800819 )
820 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200821 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800822 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200823 results.extend(input_api.canned_checks.CheckLicense(
824 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000825 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100826 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200827 r'^build[\\\/].*\.py$',
828 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700829 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100830 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200831 r'^out.*[\\\/].*\.py$',
832 r'^testing[\\\/].*\.py$',
833 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100834 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800835 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200836 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200837 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200838 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800839
nisse3d21e232016-09-02 03:07:06 -0700840 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200841 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
842 # we need to have different license checks in talk/ and webrtc/ directories.
843 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200844
tkchin3cd9a302016-06-08 12:40:28 -0700845 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
846 # ObjC subdirectories ObjC headers.
847 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100848 # Skip long-lines check for DEPS and GN files.
849 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
Artem Titova04d1402018-05-11 11:23:00 +0200850 # Also we will skip most checks for third_party directory.
Artem Titov42f0d782018-06-27 13:23:17 +0200851 third_party_filter_list = (r'^third_party[\\\/].+',)
tkchin3cd9a302016-06-08 12:40:28 -0700852 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
Artem Titova04d1402018-05-11 11:23:00 +0200853 black_list=build_file_filter_list + objc_filter_list +
854 third_party_filter_list)
tkchin3cd9a302016-06-08 12:40:28 -0700855 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
856 white_list=objc_filter_list)
Artem Titove92675b2018-05-22 10:21:27 +0200857 non_third_party_sources = lambda x: input_api.FilterSourceFile(x,
858 black_list=third_party_filter_list)
859
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000860 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700861 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
862 results.extend(input_api.canned_checks.CheckLongLines(
863 input_api, output_api, maxlen=100,
864 source_file_filter=hundred_char_sources))
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000865 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
Artem Titova04d1402018-05-11 11:23:00 +0200866 input_api, output_api, source_file_filter=non_third_party_sources))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000867 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
Artem Titova04d1402018-05-11 11:23:00 +0200868 input_api, output_api, source_file_filter=non_third_party_sources))
kjellandere5dc62a2016-12-14 00:16:21 -0800869 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
Oleh Prypine0735142018-10-04 11:15:54 +0200870 input_api, output_api, bot_whitelist=[
871 'chromium-webrtc-autoroll@webrtc-ci.iam.gserviceaccount.com'
872 ]))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000873 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
Artem Titova04d1402018-05-11 11:23:00 +0200874 input_api, output_api, source_file_filter=non_third_party_sources))
Yves Gerey87a93532018-06-20 15:51:49 +0200875 results.extend(input_api.canned_checks.CheckPatchFormatted(
876 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200877 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200878 results.extend(CheckNoIOStreamInHeaders(
879 input_api, output_api, source_file_filter=non_third_party_sources))
880 results.extend(CheckNoPragmaOnce(
881 input_api, output_api, source_file_filter=non_third_party_sources))
882 results.extend(CheckNoFRIEND_TEST(
883 input_api, output_api, source_file_filter=non_third_party_sources))
Mirko Bonadeif0e0d752018-07-04 08:48:18 +0200884 results.extend(CheckGnChanges(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200885 results.extend(CheckUnwantedDependencies(
886 input_api, output_api, source_file_filter=non_third_party_sources))
887 results.extend(CheckJSONParseErrors(
888 input_api, output_api, source_file_filter=non_third_party_sources))
charujain9893e252017-09-14 13:33:22 +0200889 results.extend(RunPythonTests(input_api, output_api))
Artem Titova04d1402018-05-11 11:23:00 +0200890 results.extend(CheckUsageOfGoogleProtobufNamespace(
891 input_api, output_api, source_file_filter=non_third_party_sources))
892 results.extend(CheckOrphanHeaders(
893 input_api, output_api, source_file_filter=non_third_party_sources))
894 results.extend(CheckNewlineAtTheEndOfProtoFiles(
895 input_api, output_api, source_file_filter=non_third_party_sources))
896 results.extend(CheckNoStreamUsageIsAdded(
Artem Titov739351d2018-05-11 12:21:36 +0200897 input_api, output_api, non_third_party_sources))
Mirko Bonadei9ce800d2019-02-05 16:48:13 +0100898 results.extend(CheckNoTestCaseUsageIsAdded(
899 input_api, output_api, non_third_party_sources))
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +0200900 results.extend(CheckAddedDepsHaveTargetApprovals(input_api, output_api))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200901 results.extend(CheckApiDepsFileIsUpToDate(input_api, output_api))
tzika06bf852018-11-15 20:37:35 +0900902 results.extend(CheckAbslMemoryInclude(
903 input_api, output_api, non_third_party_sources))
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +0200904 results.extend(CheckBannedAbslMakeUnique(
905 input_api, output_api, non_third_party_sources))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200906 return results
907
908
909def CheckApiDepsFileIsUpToDate(input_api, output_api):
Mirko Bonadei90490372018-10-26 13:17:47 +0200910 """Check that 'include_rules' in api/DEPS is up to date.
911
912 The file api/DEPS must be kept up to date in order to avoid to avoid to
913 include internal header from WebRTC's api/ headers.
914
915 This check is focused on ensuring that 'include_rules' contains a deny
916 rule for each root level directory. More focused allow rules can be
917 added to 'specific_include_rules'.
918 """
Mirko Bonadeia418e672018-10-24 13:57:25 +0200919 results = []
920 api_deps = os.path.join(input_api.PresubmitLocalPath(), 'api', 'DEPS')
921 with open(api_deps) as f:
922 deps_content = _ParseDeps(f.read())
923
924 include_rules = deps_content.get('include_rules', [])
Mirko Bonadei01e97ae2019-09-05 14:36:42 +0200925 dirs_to_skip = set(['api', 'docs'])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200926
Mirko Bonadei90490372018-10-26 13:17:47 +0200927 # Only check top level directories affected by the current CL.
928 dirs_to_check = set()
929 for f in input_api.AffectedFiles():
930 path_tokens = [t for t in f.LocalPath().split(os.sep) if t]
931 if len(path_tokens) > 1:
Mirko Bonadei01e97ae2019-09-05 14:36:42 +0200932 if (path_tokens[0] not in dirs_to_skip and
Mirko Bonadei90490372018-10-26 13:17:47 +0200933 os.path.isdir(os.path.join(input_api.PresubmitLocalPath(),
934 path_tokens[0]))):
935 dirs_to_check.add(path_tokens[0])
Mirko Bonadeia418e672018-10-24 13:57:25 +0200936
Mirko Bonadei90490372018-10-26 13:17:47 +0200937 missing_include_rules = set()
938 for p in dirs_to_check:
Mirko Bonadeia418e672018-10-24 13:57:25 +0200939 rule = '-%s' % p
940 if rule not in include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200941 missing_include_rules.add(rule)
942
Mirko Bonadeia418e672018-10-24 13:57:25 +0200943 if missing_include_rules:
Mirko Bonadei90490372018-10-26 13:17:47 +0200944 error_msg = [
945 'include_rules = [\n',
946 ' ...\n',
947 ]
Mirko Bonadeia418e672018-10-24 13:57:25 +0200948
Mirko Bonadei90490372018-10-26 13:17:47 +0200949 for r in sorted(missing_include_rules):
950 error_msg.append(' "%s",\n' % str(r))
Mirko Bonadeia418e672018-10-24 13:57:25 +0200951
Mirko Bonadei90490372018-10-26 13:17:47 +0200952 error_msg.append(' ...\n')
953 error_msg.append(']\n')
954
Mirko Bonadeia418e672018-10-24 13:57:25 +0200955 results.append(output_api.PresubmitError(
Mirko Bonadei90490372018-10-26 13:17:47 +0200956 'New root level directory detected! WebRTC api/ headers should '
957 'not #include headers from \n'
958 'the new directory, so please update "include_rules" in file\n'
959 '"%s". Example:\n%s\n' % (api_deps, ''.join(error_msg))))
960
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000961 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000962
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +0200963def CheckBannedAbslMakeUnique(input_api, output_api, source_file_filter):
964 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
965 and source_file_filter(f))
966
967 files = []
968 for f in input_api.AffectedFiles(
969 include_deletes=False, file_filter=file_filter):
970 for _, line in f.ChangedContents():
971 if 'absl::make_unique' in line:
972 files.append(f)
973 break
974
975 if len(files):
976 return [output_api.PresubmitError(
977 'Please use std::make_unique instead of absl::make_unique.\n'
978 'Affected files:',
979 files)]
980 return []
981
tzika06bf852018-11-15 20:37:35 +0900982def CheckAbslMemoryInclude(input_api, output_api, source_file_filter):
983 pattern = input_api.re.compile(
984 r'^#include\s*"absl/memory/memory.h"', input_api.re.MULTILINE)
985 file_filter = lambda f: (f.LocalPath().endswith(('.cc', '.h'))
986 and source_file_filter(f))
987
988 files = []
989 for f in input_api.AffectedFiles(
990 include_deletes=False, file_filter=file_filter):
991 contents = input_api.ReadFile(f)
992 if pattern.search(contents):
993 continue
994 for _, line in f.ChangedContents():
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +0200995 if 'absl::WrapUnique' in line:
tzika06bf852018-11-15 20:37:35 +0900996 files.append(f)
997 break
998
999 if len(files):
1000 return [output_api.PresubmitError(
Mirko Bonadei9fa8ef12019-09-17 19:14:13 +02001001 'Please include "absl/memory/memory.h" header for absl::WrapUnique.\n'
1002 'This header may or may not be included transitively depending on the '
1003 'C++ standard version.',
tzika06bf852018-11-15 20:37:35 +09001004 files)]
1005 return []
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001006
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001007def CheckChangeOnUpload(input_api, output_api):
1008 results = []
charujain9893e252017-09-14 13:33:22 +02001009 results.extend(CommonChecks(input_api, output_api))
Oleh Prypin920b6532017-10-05 11:28:51 +02001010 results.extend(CheckGnGen(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +02001011 results.extend(
1012 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +00001013 return results
1014
kjellander@webrtc.orge4158642014-08-06 09:11:18 +00001015
andrew@webrtc.org2442de12012-01-23 17:45:41 +00001016def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +00001017 results = []
charujain9893e252017-09-14 13:33:22 +02001018 results.extend(CommonChecks(input_api, output_api))
1019 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
Artem Titov42f0d782018-06-27 13:23:17 +02001020 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +00001021 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
1022 input_api, output_api))
1023 results.extend(input_api.canned_checks.CheckChangeHasDescription(
1024 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +02001025 results.extend(CheckChangeHasBugField(input_api, output_api))
1026 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +00001027 results.extend(input_api.canned_checks.CheckTreeIsOpen(
1028 input_api, output_api,
1029 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +00001030 return results
mbonadei74973ed2017-05-09 07:58:05 -07001031
1032
Artem Titova04d1402018-05-11 11:23:00 +02001033def CheckOrphanHeaders(input_api, output_api, source_file_filter):
mbonadei74973ed2017-05-09 07:58:05 -07001034 # We need to wait until we have an input_api object and use this
1035 # roundabout construct to import prebubmit_checks_lib because this file is
1036 # eval-ed and thus doesn't have __file__.
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001037 error_msg = """{} should be listed in {}."""
mbonadei74973ed2017-05-09 07:58:05 -07001038 results = []
Patrik Höglund7e60de22018-01-09 14:22:00 +01001039 orphan_blacklist = [
1040 os.path.join('tools_webrtc', 'ios', 'SDK'),
1041 ]
Oleh Prypin2f33a562017-10-04 20:17:54 +02001042 with _AddToPath(input_api.os_path.join(
1043 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')):
mbonadei74973ed2017-05-09 07:58:05 -07001044 from check_orphan_headers import GetBuildGnPathFromFilePath
1045 from check_orphan_headers import IsHeaderInBuildGn
mbonadei74973ed2017-05-09 07:58:05 -07001046
Artem Titova04d1402018-05-11 11:23:00 +02001047 file_filter = lambda x: input_api.FilterSourceFile(
1048 x, black_list=orphan_blacklist) and source_file_filter(x)
1049 for f in input_api.AffectedSourceFiles(file_filter):
Patrik Höglund7e60de22018-01-09 14:22:00 +01001050 if f.LocalPath().endswith('.h'):
mbonadei74973ed2017-05-09 07:58:05 -07001051 file_path = os.path.abspath(f.LocalPath())
1052 root_dir = os.getcwd()
1053 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
1054 root_dir)
1055 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
1056 if not in_build_gn:
1057 results.append(output_api.PresubmitError(error_msg.format(
Patrik Höglund2f3f7222017-12-19 11:08:56 +01001058 f.LocalPath(), os.path.relpath(gn_file_path))))
mbonadei74973ed2017-05-09 07:58:05 -07001059 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001060
1061
Artem Titove92675b2018-05-22 10:21:27 +02001062def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api, source_file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001063 """Checks that all .proto files are terminated with a newline."""
1064 error_msg = 'File {} must end with exactly one newline.'
1065 results = []
Artem Titova04d1402018-05-11 11:23:00 +02001066 file_filter = lambda x: input_api.FilterSourceFile(
1067 x, white_list=(r'.+\.proto$',)) and source_file_filter(x)
1068 for f in input_api.AffectedSourceFiles(file_filter):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001069 file_path = f.LocalPath()
1070 with open(file_path) as f:
1071 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +02001072 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +02001073 results.append(output_api.PresubmitError(error_msg.format(file_path)))
1074 return results
Mirko Bonadei7e4ee6e2018-09-28 11:45:23 +02001075
1076
1077def _ExtractAddRulesFromParsedDeps(parsed_deps):
1078 """Extract the rules that add dependencies from a parsed DEPS file.
1079
1080 Args:
1081 parsed_deps: the locals dictionary from evaluating the DEPS file."""
1082 add_rules = set()
1083 add_rules.update([
1084 rule[1:] for rule in parsed_deps.get('include_rules', [])
1085 if rule.startswith('+') or rule.startswith('!')
1086 ])
1087 for _, rules in parsed_deps.get('specific_include_rules',
1088 {}).iteritems():
1089 add_rules.update([
1090 rule[1:] for rule in rules
1091 if rule.startswith('+') or rule.startswith('!')
1092 ])
1093 return add_rules
1094
1095
1096def _ParseDeps(contents):
1097 """Simple helper for parsing DEPS files."""
1098 # Stubs for handling special syntax in the root DEPS file.
1099 class VarImpl(object):
1100
1101 def __init__(self, local_scope):
1102 self._local_scope = local_scope
1103
1104 def Lookup(self, var_name):
1105 """Implements the Var syntax."""
1106 try:
1107 return self._local_scope['vars'][var_name]
1108 except KeyError:
1109 raise Exception('Var is not defined: %s' % var_name)
1110
1111 local_scope = {}
1112 global_scope = {
1113 'Var': VarImpl(local_scope).Lookup,
1114 }
1115 exec contents in global_scope, local_scope
1116 return local_scope
1117
1118
1119def _CalculateAddedDeps(os_path, old_contents, new_contents):
1120 """Helper method for _CheckAddedDepsHaveTargetApprovals. Returns
1121 a set of DEPS entries that we should look up.
1122
1123 For a directory (rather than a specific filename) we fake a path to
1124 a specific filename by adding /DEPS. This is chosen as a file that
1125 will seldom or never be subject to per-file include_rules.
1126 """
1127 # We ignore deps entries on auto-generated directories.
1128 auto_generated_dirs = ['grit', 'jni']
1129
1130 old_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(old_contents))
1131 new_deps = _ExtractAddRulesFromParsedDeps(_ParseDeps(new_contents))
1132
1133 added_deps = new_deps.difference(old_deps)
1134
1135 results = set()
1136 for added_dep in added_deps:
1137 if added_dep.split('/')[0] in auto_generated_dirs:
1138 continue
1139 # Assume that a rule that ends in .h is a rule for a specific file.
1140 if added_dep.endswith('.h'):
1141 results.add(added_dep)
1142 else:
1143 results.add(os_path.join(added_dep, 'DEPS'))
1144 return results
1145
1146
1147def CheckAddedDepsHaveTargetApprovals(input_api, output_api):
1148 """When a dependency prefixed with + is added to a DEPS file, we
1149 want to make sure that the change is reviewed by an OWNER of the
1150 target file or directory, to avoid layering violations from being
1151 introduced. This check verifies that this happens.
1152 """
1153 virtual_depended_on_files = set()
1154
1155 file_filter = lambda f: not input_api.re.match(
1156 r"^third_party[\\\/](WebKit|blink)[\\\/].*", f.LocalPath())
1157 for f in input_api.AffectedFiles(include_deletes=False,
1158 file_filter=file_filter):
1159 filename = input_api.os_path.basename(f.LocalPath())
1160 if filename == 'DEPS':
1161 virtual_depended_on_files.update(_CalculateAddedDeps(
1162 input_api.os_path,
1163 '\n'.join(f.OldContents()),
1164 '\n'.join(f.NewContents())))
1165
1166 if not virtual_depended_on_files:
1167 return []
1168
1169 if input_api.is_committing:
1170 if input_api.tbr:
1171 return [output_api.PresubmitNotifyResult(
1172 '--tbr was specified, skipping OWNERS check for DEPS additions')]
1173 if input_api.dry_run:
1174 return [output_api.PresubmitNotifyResult(
1175 'This is a dry run, skipping OWNERS check for DEPS additions')]
1176 if not input_api.change.issue:
1177 return [output_api.PresubmitError(
1178 "DEPS approval by OWNERS check failed: this change has "
1179 "no change number, so we can't check it for approvals.")]
1180 output = output_api.PresubmitError
1181 else:
1182 output = output_api.PresubmitNotifyResult
1183
1184 owners_db = input_api.owners_db
1185 owner_email, reviewers = (
1186 input_api.canned_checks.GetCodereviewOwnerAndReviewers(
1187 input_api,
1188 owners_db.email_regexp,
1189 approval_needed=input_api.is_committing))
1190
1191 owner_email = owner_email or input_api.change.author_email
1192
1193 reviewers_plus_owner = set(reviewers)
1194 if owner_email:
1195 reviewers_plus_owner.add(owner_email)
1196 missing_files = owners_db.files_not_covered_by(virtual_depended_on_files,
1197 reviewers_plus_owner)
1198
1199 # We strip the /DEPS part that was added by
1200 # _FilesToCheckForIncomingDeps to fake a path to a file in a
1201 # directory.
1202 def StripDeps(path):
1203 start_deps = path.rfind('/DEPS')
1204 if start_deps != -1:
1205 return path[:start_deps]
1206 else:
1207 return path
1208 unapproved_dependencies = ["'+%s'," % StripDeps(path)
1209 for path in missing_files]
1210
1211 if unapproved_dependencies:
1212 output_list = [
1213 output('You need LGTM from owners of depends-on paths in DEPS that were '
1214 'modified in this CL:\n %s' %
1215 '\n '.join(sorted(unapproved_dependencies)))]
1216 suggested_owners = owners_db.reviewers_for(missing_files, owner_email)
1217 output_list.append(output(
1218 'Suggested missing target path OWNERS:\n %s' %
1219 '\n '.join(suggested_owners or [])))
1220 return output_list
1221
1222 return []