blob: 1666c6b905ccc7203b0cf3cdb0f9fd3ca069a07a [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
ehmaldonado4fb97462017-01-30 05:27:22 -080012import subprocess
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000013import sys
kjellander@webrtc.org85759802013-10-22 16:47:40 +000014
15
oprypin2aa463f2017-03-23 03:17:02 -070016# Files and directories that are *skipped* by cpplint in the presubmit script.
17CPPLINT_BLACKLIST = [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +020018 'tools_webrtc',
ilnikd60d06a2017-04-05 03:02:20 -070019 'webrtc/api/video_codecs/video_decoder.h',
oprypin2aa463f2017-03-23 03:17:02 -070020 'webrtc/examples/objc',
21 'webrtc/media',
22 'webrtc/modules/audio_coding',
23 'webrtc/modules/audio_conference_mixer',
24 'webrtc/modules/audio_device',
25 'webrtc/modules/audio_processing',
26 'webrtc/modules/desktop_capture',
27 'webrtc/modules/include/module_common_types.h',
28 'webrtc/modules/media_file',
29 'webrtc/modules/utility',
30 'webrtc/modules/video_capture',
31 'webrtc/p2p',
32 'webrtc/pc',
Henrik Kjellanderc0362762017-06-29 08:03:04 +020033 'webrtc/rtc_base',
oprypin2aa463f2017-03-23 03:17:02 -070034 'webrtc/sdk/android/src/jni',
35 'webrtc/sdk/objc',
36 'webrtc/system_wrappers',
37 'webrtc/test',
38 'webrtc/voice_engine',
oprypin2aa463f2017-03-23 03:17:02 -070039 'webrtc/common_types.h',
40 'webrtc/common_types.cc',
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.
kjellandere5a87a52016-04-27 02:32:12 -070050# - whitespace/operators: Same as above (doesn't seem sufficient to eliminate
51# all move-related errors).
jbauchc4e3ead2016-02-19 00:25:55 -080052BLACKLIST_LINT_FILTERS = [
53 '-build/c++11',
kjellandere5a87a52016-04-27 02:32:12 -070054 '-whitespace/operators',
jbauchc4e3ead2016-02-19 00:25:55 -080055]
56
kjellanderfd595232015-12-04 02:44:09 -080057# List of directories of "supported" native APIs. That means changes to headers
58# will be done in a compatible way following this scheme:
59# 1. Non-breaking changes are made.
60# 2. The old APIs as marked as deprecated (with comments).
61# 3. Deprecation is announced to discuss-webrtc@googlegroups.com and
62# webrtc-users@google.com (internal list).
63# 4. (later) The deprecated APIs are removed.
kjellander53047c92015-12-02 23:56:14 -080064NATIVE_API_DIRS = (
kjellander53047c92015-12-02 23:56:14 -080065 'webrtc',
kjellanderdd705472016-06-09 11:17:27 -070066 'webrtc/api',
67 'webrtc/media',
kjellander53047c92015-12-02 23:56:14 -080068 'webrtc/modules/audio_device/include',
kjellanderdd705472016-06-09 11:17:27 -070069 'webrtc/pc',
70)
71# These directories should not be used but are maintained only to avoid breaking
72# some legacy downstream code.
73LEGACY_API_DIRS = (
kjellanderdd705472016-06-09 11:17:27 -070074 'webrtc/common_audio/include',
75 'webrtc/modules/audio_coding/include',
76 'webrtc/modules/audio_conference_mixer/include',
kjellander53047c92015-12-02 23:56:14 -080077 'webrtc/modules/audio_processing/include',
78 'webrtc/modules/bitrate_controller/include',
Stefan Holmer80e12072016-02-23 13:30:42 +010079 'webrtc/modules/congestion_controller/include',
kjellander53047c92015-12-02 23:56:14 -080080 'webrtc/modules/include',
81 'webrtc/modules/remote_bitrate_estimator/include',
82 'webrtc/modules/rtp_rtcp/include',
kjellanderdd705472016-06-09 11:17:27 -070083 'webrtc/modules/rtp_rtcp/source',
kjellander53047c92015-12-02 23:56:14 -080084 'webrtc/modules/utility/include',
85 'webrtc/modules/video_coding/codecs/h264/include',
86 'webrtc/modules/video_coding/codecs/i420/include',
87 'webrtc/modules/video_coding/codecs/vp8/include',
88 'webrtc/modules/video_coding/codecs/vp9/include',
89 'webrtc/modules/video_coding/include',
ehmaldonadof6a861a2017-07-19 10:40:47 -070090 'webrtc/rtc_base',
kjellanderdd705472016-06-09 11:17:27 -070091 'webrtc/system_wrappers/include',
kjellander53047c92015-12-02 23:56:14 -080092 'webrtc/voice_engine/include',
93)
kjellanderdd705472016-06-09 11:17:27 -070094API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080095
96
ehmaldonado4fb97462017-01-30 05:27:22 -080097def _RunCommand(command, cwd):
98 """Runs a command and returns the output from that command."""
99 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
100 cwd=cwd)
101 stdout = p.stdout.read()
102 stderr = p.stderr.read()
103 p.wait()
104 p.stdout.close()
105 p.stderr.close()
106 return p.returncode, stdout, stderr
107
108
charujain9893e252017-09-14 13:33:22 +0200109def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800110 """Ensures the list of native API header directories is up to date."""
111 non_existing_paths = []
112 native_api_full_paths = [
113 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700114 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800115 for path in native_api_full_paths:
116 if not os.path.isdir(path):
117 non_existing_paths.append(path)
118 if non_existing_paths:
119 return [output_api.PresubmitError(
120 'Directories to native API headers have changed which has made the '
121 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
122 'location of our native APIs.',
123 non_existing_paths)]
124 return []
125
kjellanderc88b5d52017-04-05 06:42:43 -0700126API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700127You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800128 1. Make compatible changes that don't break existing clients. Usually
129 this is done by keeping the existing method signatures unchanged.
130 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700131 3. Create a timeline and plan for when the deprecated stuff will be
132 removed. (The amount of time we give users to change their code
133 should be informed by how much work it is for them. If they just
134 need to replace one name with another or something equally
135 simple, 1-2 weeks might be good; if they need to do serious work,
136 up to 3 months may be called for.)
137 4. Update/inform existing downstream code owners to stop using the
138 deprecated stuff. (Send announcements to
139 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
140 5. Remove the deprecated stuff, once the agreed-upon amount of time
141 has passed.
142Related files:
143"""
kjellander53047c92015-12-02 23:56:14 -0800144
charujain9893e252017-09-14 13:33:22 +0200145def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800146 """Checks to remind proper changing of native APIs."""
147 files = []
148 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
149 if f.LocalPath().endswith('.h'):
kjellanderdd705472016-06-09 11:17:27 -0700150 for path in API_DIRS:
kjellander53047c92015-12-02 23:56:14 -0800151 if os.path.dirname(f.LocalPath()) == path:
152 files.append(f)
153
154 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700155 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800156 return []
157
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100158
charujain9893e252017-09-14 13:33:22 +0200159def CheckNoIOStreamInHeaders(input_api, output_api):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000160 """Checks to make sure no .h files include <iostream>."""
161 files = []
162 pattern = input_api.re.compile(r'^#include\s*<iostream>',
163 input_api.re.MULTILINE)
164 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
165 if not f.LocalPath().endswith('.h'):
166 continue
167 contents = input_api.ReadFile(f)
168 if pattern.search(contents):
169 files.append(f)
170
171 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200172 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000173 'Do not #include <iostream> in header files, since it inserts static ' +
174 'initialization into every file including the header. Instead, ' +
175 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200176 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000177 return []
178
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000179
charujain9893e252017-09-14 13:33:22 +0200180def CheckNoPragmaOnce(input_api, output_api):
kjellander6aeef742017-02-20 01:13:18 -0800181 """Make sure that banned functions are not used."""
182 files = []
183 pattern = input_api.re.compile(r'^#pragma\s+once',
184 input_api.re.MULTILINE)
185 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
186 if not f.LocalPath().endswith('.h'):
187 continue
188 contents = input_api.ReadFile(f)
189 if pattern.search(contents):
190 files.append(f)
191
192 if files:
193 return [output_api.PresubmitError(
194 'Do not use #pragma once in header files.\n'
195 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
196 files)]
197 return []
198
199
charujain9893e252017-09-14 13:33:22 +0200200def CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000201 """Make sure that gtest's FRIEND_TEST() macro is not used, the
202 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
203 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
204 problems = []
205
206 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
207 for f in input_api.AffectedFiles(file_filter=file_filter):
208 for line_num, line in f.ChangedContents():
209 if 'FRIEND_TEST(' in line:
210 problems.append(' %s:%d' % (f.LocalPath(), line_num))
211
212 if not problems:
213 return []
214 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
215 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
216 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
217
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000218
charujain9893e252017-09-14 13:33:22 +0200219def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700220 """ Checks if a file is blacklisted for lint check."""
221 for path in blacklist_paths:
222 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100223 return True
224 return False
225
226
charujain9893e252017-09-14 13:33:22 +0200227def CheckApprovedFilesLintClean(input_api, output_api,
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000228 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700229 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200230 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000231 depot_tools/presubmit_canned_checks.py but has less filters and only checks
232 added files."""
233 result = []
234
235 # Initialize cpplint.
236 import cpplint
237 # Access to a protected member _XX of a client class
238 # pylint: disable=W0212
239 cpplint._cpplint_state.ResetErrorCounts()
240
jbauchc4e3ead2016-02-19 00:25:55 -0800241 lint_filters = cpplint._Filters()
242 lint_filters.extend(BLACKLIST_LINT_FILTERS)
243 cpplint._SetFilters(','.join(lint_filters))
244
oprypin2aa463f2017-03-23 03:17:02 -0700245 # Create a platform independent blacklist for cpplint.
246 blacklist_paths = [input_api.os_path.join(*path.split('/'))
247 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100248
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000249 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700250 # default when running cpplint.py from command line. To make it possible to
251 # work with not-yet-converted code, we're only applying it to new (or
252 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000253 verbosity_level = 1
254 files = []
255 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200256 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200257 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
oprypin2aa463f2017-03-23 03:17:02 -0700258 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000259 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000260
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000261 for file_name in files:
262 cpplint.ProcessFile(file_name, verbosity_level)
263
264 if cpplint._cpplint_state.error_count > 0:
265 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700266 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000267 else:
268 res_type = output_api.PresubmitPromptWarning
269 result = [res_type('Changelist failed cpplint.py check.')]
270
271 return result
272
charujain9893e252017-09-14 13:33:22 +0200273def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700274 # Disallow referencing source files with paths above the GN file location.
275 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
276 re.MULTILINE | re.DOTALL)
277 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
278 violating_gn_files = set()
279 violating_source_entries = []
280 for gn_file in gn_files:
281 contents = input_api.ReadFile(gn_file)
282 for source_block_match in source_pattern.finditer(contents):
283 # Find all source list entries starting with ../ in the source block
284 # (exclude overrides entries).
285 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
286 source_file = file_list_match.group(1)
287 if 'overrides/' not in source_file:
288 violating_source_entries.append(source_file)
289 violating_gn_files.add(gn_file)
290 if violating_gn_files:
291 return [output_api.PresubmitError(
292 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100293 'allowed. Please introduce new GN targets in the proper location '
294 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700295 'Invalid source entries:\n'
296 '%s\n'
297 'Violating GN files:' % '\n'.join(violating_source_entries),
298 items=violating_gn_files)]
299 return []
300
charujain9893e252017-09-14 13:33:22 +0200301def CheckNoMixingCAndCCSources(input_api, gn_files, output_api):
kjellander7439f972016-12-05 22:47:46 -0800302 # Disallow mixing .c and .cc source files in the same target.
303 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
304 re.MULTILINE | re.DOTALL)
305 file_pattern = input_api.re.compile(r'"(.*)"')
306 violating_gn_files = dict()
307 for gn_file in gn_files:
308 contents = input_api.ReadFile(gn_file)
309 for source_block_match in source_pattern.finditer(contents):
310 c_files = []
311 cc_files = []
312 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
313 source_file = file_list_match.group(1)
314 if source_file.endswith('.c'):
315 c_files.append(source_file)
316 if source_file.endswith('.cc'):
317 cc_files.append(source_file)
318 if c_files and cc_files:
319 violating_gn_files[gn_file.LocalPath()] = sorted(c_files + cc_files)
320 if violating_gn_files:
321 return [output_api.PresubmitError(
322 'GN targets cannot mix .cc and .c source files. Please create a '
323 'separate target for each collection of sources.\n'
324 'Mixed sources: \n'
325 '%s\n'
326 'Violating GN files:' % json.dumps(violating_gn_files, indent=2),
327 items=violating_gn_files.keys())]
328 return []
329
charujain9893e252017-09-14 13:33:22 +0200330def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800331 cwd = input_api.PresubmitLocalPath()
mbonadeiab587dc2017-05-12 04:13:31 -0700332 script_path = os.path.join('tools_webrtc', 'presubmit_checks_lib',
333 'check_package_boundaries.py')
ehmaldonado4fb97462017-01-30 05:27:22 -0800334 webrtc_path = os.path.join('webrtc')
335 command = [sys.executable, script_path, webrtc_path]
336 command += [gn_file.LocalPath() for gn_file in gn_files]
337 returncode, _, stderr = _RunCommand(command, cwd)
338 if returncode:
339 return [output_api.PresubmitError(
340 'There are package boundary violations in the following GN files:\n\n'
341 '%s' % stderr)]
342 return []
343
charujain9893e252017-09-14 13:33:22 +0200344def CheckGnChanges(input_api, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700345 source_file_filter = lambda x: input_api.FilterSourceFile(
346 x, white_list=(r'.+\.(gn|gni)$',))
347
348 gn_files = []
349 for f in input_api.AffectedSourceFiles(source_file_filter):
350 if f.LocalPath().startswith('webrtc'):
351 gn_files.append(f)
352
353 result = []
354 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200355 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
356 result.extend(CheckNoMixingCAndCCSources(input_api, gn_files, output_api))
357 result.extend(CheckNoPackageBoundaryViolations(
ehmaldonado4fb97462017-01-30 05:27:22 -0800358 input_api, gn_files, output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700359 return result
360
charujain9893e252017-09-14 13:33:22 +0200361def CheckUnwantedDependencies(input_api, output_api):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000362 """Runs checkdeps on #include statements added in this
363 change. Breaking - rules is an error, breaking ! rules is a
364 warning.
365 """
366 # Copied from Chromium's src/PRESUBMIT.py.
367
368 # We need to wait until we have an input_api object and use this
369 # roundabout construct to import checkdeps because this file is
370 # eval-ed and thus doesn't have __file__.
371 original_sys_path = sys.path
372 try:
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +0000373 checkdeps_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
374 'buildtools', 'checkdeps')
375 if not os.path.exists(checkdeps_path):
376 return [output_api.PresubmitError(
377 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
378 'download Chromium and setup the symlinks?' % checkdeps_path)]
379 sys.path.append(checkdeps_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000380 import checkdeps
381 from cpp_checker import CppChecker
382 from rules import Rule
383 finally:
384 # Restore sys.path to what it was before.
385 sys.path = original_sys_path
386
387 added_includes = []
388 for f in input_api.AffectedFiles():
389 if not CppChecker.IsCppFile(f.LocalPath()):
390 continue
391
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200392 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000393 added_includes.append([f.LocalPath(), changed_lines])
394
395 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
396
397 error_descriptions = []
398 warning_descriptions = []
399 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
400 added_includes):
401 description_with_path = '%s\n %s' % (path, rule_description)
402 if rule_type == Rule.DISALLOW:
403 error_descriptions.append(description_with_path)
404 else:
405 warning_descriptions.append(description_with_path)
406
407 results = []
408 if error_descriptions:
409 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700410 'You added one or more #includes that violate checkdeps rules.\n'
411 'Check that the DEPS files in these locations contain valid rules.\n'
412 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
413 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000414 error_descriptions))
415 if warning_descriptions:
416 results.append(output_api.PresubmitPromptOrNotify(
417 'You added one or more #includes of files that are temporarily\n'
418 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700419 '#include? See relevant DEPS file(s) for details and contacts.\n'
420 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
421 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000422 warning_descriptions))
423 return results
424
charujain9893e252017-09-14 13:33:22 +0200425def CheckCommitMessageBugEntry(input_api, output_api):
426 """Check that bug entries are well-formed in commit message."""
427 bogus_bug_msg = (
428 'Bogus BUG entry: %s. Please specify the issue tracker prefix and the '
429 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
430 results = []
431 for bug in (input_api.change.BUG or '').split(','):
432 bug = bug.strip()
433 if bug.lower() == 'none':
434 continue
435 if ':' not in bug:
436 try:
437 if int(bug) > 100000:
438 # Rough indicator for current chromium bugs.
439 prefix_guess = 'chromium'
440 else:
441 prefix_guess = 'webrtc'
442 results.append('BUG entry requires issue tracker prefix, e.g. %s:%s' %
443 (prefix_guess, bug))
444 except ValueError:
445 results.append(bogus_bug_msg % bug)
446 elif not re.match(r'\w+:\d+', bug):
447 results.append(bogus_bug_msg % bug)
448 return [output_api.PresubmitError(r) for r in results]
449
450def CheckChangeHasBugField(input_api, output_api):
kjellanderd1e26a92016-09-19 08:11:16 -0700451 """Requires that the changelist have a BUG= field.
452
453 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
454 since it fails the presubmit if the BUG= field is missing or doesn't contain
455 a bug reference.
456 """
457 if input_api.change.BUG:
458 return []
459 else:
460 return [output_api.PresubmitError(
461 'The BUG=[bug number] field is mandatory. Please create a bug and '
462 'reference it using either of:\n'
463 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n'
464 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000465
charujain9893e252017-09-14 13:33:22 +0200466def CheckJSONParseErrors(input_api, output_api):
kjellander569cf942016-02-11 05:02:59 -0800467 """Check that JSON files do not contain syntax errors."""
468
469 def FilterFile(affected_file):
470 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
471
472 def GetJSONParseError(input_api, filename):
473 try:
474 contents = input_api.ReadFile(filename)
475 input_api.json.loads(contents)
476 except ValueError as e:
477 return e
478 return None
479
480 results = []
481 for affected_file in input_api.AffectedFiles(
482 file_filter=FilterFile, include_deletes=False):
483 parse_error = GetJSONParseError(input_api,
484 affected_file.AbsoluteLocalPath())
485 if parse_error:
486 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
487 (affected_file.LocalPath(), parse_error)))
488 return results
489
490
charujain9893e252017-09-14 13:33:22 +0200491def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700492 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200493 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
494
495 test_directories = [
charujain9893e252017-09-14 13:33:22 +0200496 '/',
kjellander41bafb22017-06-30 04:14:54 -0700497 Join('webrtc', 'rtc_tools', 'py_event_log_analyzer'),
498 Join('webrtc', 'rtc_tools'),
oprypinabd101b2017-04-06 23:21:30 -0700499 Join('webrtc', 'audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800500 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200501 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800502 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200503 ]
504
505 tests = []
506 for directory in test_directories:
507 tests.extend(
508 input_api.canned_checks.GetUnitTestsInDirectory(
509 input_api,
510 output_api,
511 directory,
512 whitelist=[r'.+_test\.py$']))
513 return input_api.RunTests(tests, parallel=True)
514
515
charujain9893e252017-09-14 13:33:22 +0200516def CheckUsageOfGoogleProtobufNamespace(input_api, output_api):
mbonadei38415b22017-04-07 05:38:01 -0700517 """Checks that the namespace google::protobuf has not been used."""
518 files = []
519 pattern = input_api.re.compile(r'google::protobuf')
mbonadei01410dc2017-09-08 08:01:53 -0700520 proto_utils_path = os.path.join('webrtc', 'rtc_base', 'protobuf_utils.h')
mbonadei38415b22017-04-07 05:38:01 -0700521 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
522 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
523 continue
524 contents = input_api.ReadFile(f)
525 if pattern.search(contents):
526 files.append(f)
527
528 if files:
529 return [output_api.PresubmitError(
530 'Please avoid to use namespace `google::protobuf` directly.\n'
531 'Add a using directive in `%s` and include that header instead.'
532 % proto_utils_path, files)]
533 return []
534
535
charujain9893e252017-09-14 13:33:22 +0200536def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000537 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000538 results = []
tkchin42f580e2015-11-26 23:18:23 -0800539 # Filter out files that are in objc or ios dirs from being cpplint-ed since
540 # they do not follow C++ lint rules.
541 black_list = input_api.DEFAULT_BLACK_LIST + (
542 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000543 r".*objc\.[hcm]+$",
hjon65ae2d82016-08-02 23:55:44 -0700544 r"webrtc\/build\/ios\/SDK\/.*",
tkchin42f580e2015-11-26 23:18:23 -0800545 )
546 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200547 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800548 input_api, output_api, source_file_filter))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000549 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100550 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200551 r'^build[\\\/].*\.py$',
552 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700553 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100554 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200555 r'^out.*[\\\/].*\.py$',
556 r'^testing[\\\/].*\.py$',
557 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100558 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800559 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200560 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
561 r'^tools_webrtc[\\\/]valgrind[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200562 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200563 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800564
nisse3d21e232016-09-02 03:07:06 -0700565 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200566 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
567 # we need to have different license checks in talk/ and webrtc/ directories.
568 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200569
tkchin3cd9a302016-06-08 12:40:28 -0700570 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
571 # ObjC subdirectories ObjC headers.
572 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100573 # Skip long-lines check for DEPS and GN files.
574 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
tkchin3cd9a302016-06-08 12:40:28 -0700575 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
576 black_list=build_file_filter_list + objc_filter_list)
577 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
578 white_list=objc_filter_list)
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000579 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700580 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
581 results.extend(input_api.canned_checks.CheckLongLines(
582 input_api, output_api, maxlen=100,
583 source_file_filter=hundred_char_sources))
584
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000585 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
586 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000587 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
588 input_api, output_api))
kjellandere5dc62a2016-12-14 00:16:21 -0800589 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
590 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000591 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
592 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200593 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
594 results.extend(CheckNoIOStreamInHeaders(input_api, output_api))
595 results.extend(CheckNoPragmaOnce(input_api, output_api))
596 results.extend(CheckNoFRIEND_TEST(input_api, output_api))
597 results.extend(CheckGnChanges(input_api, output_api))
598 results.extend(CheckUnwantedDependencies(input_api, output_api))
599 results.extend(CheckJSONParseErrors(input_api, output_api))
600 results.extend(RunPythonTests(input_api, output_api))
601 results.extend(CheckUsageOfGoogleProtobufNamespace(input_api, output_api))
602 results.extend(CheckOrphanHeaders(input_api, output_api))
603 results.extend(CheckNewLineAtTheEndOfProtoFiles(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000604 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000605
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000606
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000607def CheckChangeOnUpload(input_api, output_api):
608 results = []
charujain9893e252017-09-14 13:33:22 +0200609 results.extend(CommonChecks(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200610 results.extend(
611 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000612 return results
613
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000614
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000615def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000616 results = []
charujain9893e252017-09-14 13:33:22 +0200617 results.extend(CommonChecks(input_api, output_api))
618 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
niklase@google.com1198db92011-06-09 07:07:24 +0000619 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000620 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
621 input_api, output_api))
622 results.extend(input_api.canned_checks.CheckChangeHasDescription(
623 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200624 results.extend(CheckChangeHasBugField(input_api, output_api))
625 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000626 results.extend(input_api.canned_checks.CheckTreeIsOpen(
627 input_api, output_api,
628 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000629 return results
mbonadei74973ed2017-05-09 07:58:05 -0700630
631
charujain9893e252017-09-14 13:33:22 +0200632def CheckOrphanHeaders(input_api, output_api):
mbonadei74973ed2017-05-09 07:58:05 -0700633 # We need to wait until we have an input_api object and use this
634 # roundabout construct to import prebubmit_checks_lib because this file is
635 # eval-ed and thus doesn't have __file__.
636 error_msg = """Header file {} is not listed in any GN target.
637 Please create a target or add it to an existing one in {}"""
638 results = []
639 original_sys_path = sys.path
640 try:
641 sys.path = sys.path + [input_api.os_path.join(
642 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')]
643 from check_orphan_headers import GetBuildGnPathFromFilePath
644 from check_orphan_headers import IsHeaderInBuildGn
645 finally:
646 # Restore sys.path to what it was before.
647 sys.path = original_sys_path
648
649 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
mbonadeia644ad32017-05-10 05:21:55 -0700650 if f.LocalPath().endswith('.h') and f.Action() == 'A':
mbonadei74973ed2017-05-09 07:58:05 -0700651 file_path = os.path.abspath(f.LocalPath())
652 root_dir = os.getcwd()
653 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
654 root_dir)
655 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
656 if not in_build_gn:
657 results.append(output_api.PresubmitError(error_msg.format(
658 file_path, gn_file_path)))
659 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200660
661
charujain9893e252017-09-14 13:33:22 +0200662def CheckNewLineAtTheEndOfProtoFiles(input_api, output_api):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200663 """Checks that all .proto files are terminated with a newline."""
664 error_msg = 'File {} must end with exactly one newline.'
665 results = []
666 source_file_filter = lambda x: input_api.FilterSourceFile(
667 x, white_list=(r'.+\.proto$',))
668 for f in input_api.AffectedSourceFiles(source_file_filter):
669 file_path = f.LocalPath()
670 with open(file_path) as f:
671 lines = f.readlines()
672 if lines[-1] != '\n' or lines[-2] == '\n':
673 results.append(output_api.PresubmitError(error_msg.format(file_path)))
674 return results