blob: 37047d47cef0b83d15e00f73c3a82168157cc676 [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 = [
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',
22 'media',
23 'modules/audio_coding',
24 'modules/audio_conference_mixer',
25 'modules/audio_device',
26 'modules/audio_processing',
27 'modules/desktop_capture',
28 'modules/include/module_common_types.h',
29 'modules/media_file',
30 'modules/utility',
31 'modules/video_capture',
32 'p2p',
33 'pc',
34 '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.
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 = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020065 'api',
66 'media',
67 'modules/audio_device/include',
68 'pc',
kjellanderdd705472016-06-09 11:17:27 -070069)
70# These directories should not be used but are maintained only to avoid breaking
71# some legacy downstream code.
72LEGACY_API_DIRS = (
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020073 'common_audio/include',
74 'modules/audio_coding/include',
75 'modules/audio_conference_mixer/include',
76 'modules/audio_processing/include',
77 'modules/bitrate_controller/include',
78 'modules/congestion_controller/include',
79 'modules/include',
80 'modules/remote_bitrate_estimator/include',
81 'modules/rtp_rtcp/include',
82 'modules/rtp_rtcp/source',
83 'modules/utility/include',
84 'modules/video_coding/codecs/h264/include',
85 'modules/video_coding/codecs/i420/include',
86 'modules/video_coding/codecs/vp8/include',
87 'modules/video_coding/codecs/vp9/include',
88 'modules/video_coding/include',
89 'rtc_base',
90 'system_wrappers/include',
91 'voice_engine/include',
kjellander53047c92015-12-02 23:56:14 -080092)
kjellanderdd705472016-06-09 11:17:27 -070093API_DIRS = NATIVE_API_DIRS[:] + LEGACY_API_DIRS[:]
kjellander53047c92015-12-02 23:56:14 -080094
95
ehmaldonado4fb97462017-01-30 05:27:22 -080096def _RunCommand(command, cwd):
97 """Runs a command and returns the output from that command."""
98 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
99 cwd=cwd)
100 stdout = p.stdout.read()
101 stderr = p.stderr.read()
102 p.wait()
103 p.stdout.close()
104 p.stderr.close()
105 return p.returncode, stdout, stderr
106
107
charujain9893e252017-09-14 13:33:22 +0200108def VerifyNativeApiHeadersListIsValid(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800109 """Ensures the list of native API header directories is up to date."""
110 non_existing_paths = []
111 native_api_full_paths = [
112 input_api.os_path.join(input_api.PresubmitLocalPath(),
kjellanderdd705472016-06-09 11:17:27 -0700113 *path.split('/')) for path in API_DIRS]
kjellander53047c92015-12-02 23:56:14 -0800114 for path in native_api_full_paths:
115 if not os.path.isdir(path):
116 non_existing_paths.append(path)
117 if non_existing_paths:
118 return [output_api.PresubmitError(
119 'Directories to native API headers have changed which has made the '
120 'list in PRESUBMIT.py outdated.\nPlease update it to the current '
121 'location of our native APIs.',
122 non_existing_paths)]
123 return []
124
kjellanderc88b5d52017-04-05 06:42:43 -0700125API_CHANGE_MSG = """
kwibergeb133022016-04-07 07:41:48 -0700126You seem to be changing native API header files. Please make sure that you:
oprypin375b9ac2017-02-13 04:13:23 -0800127 1. Make compatible changes that don't break existing clients. Usually
128 this is done by keeping the existing method signatures unchanged.
129 2. Mark the old stuff as deprecated (see RTC_DEPRECATED macro).
kwibergeb133022016-04-07 07:41:48 -0700130 3. Create a timeline and plan for when the deprecated stuff will be
131 removed. (The amount of time we give users to change their code
132 should be informed by how much work it is for them. If they just
133 need to replace one name with another or something equally
134 simple, 1-2 weeks might be good; if they need to do serious work,
135 up to 3 months may be called for.)
136 4. Update/inform existing downstream code owners to stop using the
137 deprecated stuff. (Send announcements to
138 discuss-webrtc@googlegroups.com and webrtc-users@google.com.)
139 5. Remove the deprecated stuff, once the agreed-upon amount of time
140 has passed.
141Related files:
142"""
kjellander53047c92015-12-02 23:56:14 -0800143
charujain9893e252017-09-14 13:33:22 +0200144def CheckNativeApiHeaderChanges(input_api, output_api):
kjellander53047c92015-12-02 23:56:14 -0800145 """Checks to remind proper changing of native APIs."""
146 files = []
147 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
148 if f.LocalPath().endswith('.h'):
kjellanderdd705472016-06-09 11:17:27 -0700149 for path in API_DIRS:
kjellander53047c92015-12-02 23:56:14 -0800150 if os.path.dirname(f.LocalPath()) == path:
151 files.append(f)
152
153 if files:
kjellanderc88b5d52017-04-05 06:42:43 -0700154 return [output_api.PresubmitNotifyResult(API_CHANGE_MSG, files)]
kjellander53047c92015-12-02 23:56:14 -0800155 return []
156
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100157
charujain9893e252017-09-14 13:33:22 +0200158def CheckNoIOStreamInHeaders(input_api, output_api):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000159 """Checks to make sure no .h files include <iostream>."""
160 files = []
161 pattern = input_api.re.compile(r'^#include\s*<iostream>',
162 input_api.re.MULTILINE)
163 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
164 if not f.LocalPath().endswith('.h'):
165 continue
166 contents = input_api.ReadFile(f)
167 if pattern.search(contents):
168 files.append(f)
169
170 if len(files):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200171 return [output_api.PresubmitError(
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000172 'Do not #include <iostream> in header files, since it inserts static ' +
173 'initialization into every file including the header. Instead, ' +
174 '#include <ostream>. See http://crbug.com/94794',
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200175 files)]
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000176 return []
177
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000178
charujain9893e252017-09-14 13:33:22 +0200179def CheckNoPragmaOnce(input_api, output_api):
kjellander6aeef742017-02-20 01:13:18 -0800180 """Make sure that banned functions are not used."""
181 files = []
182 pattern = input_api.re.compile(r'^#pragma\s+once',
183 input_api.re.MULTILINE)
184 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
185 if not f.LocalPath().endswith('.h'):
186 continue
187 contents = input_api.ReadFile(f)
188 if pattern.search(contents):
189 files.append(f)
190
191 if files:
192 return [output_api.PresubmitError(
193 'Do not use #pragma once in header files.\n'
194 'See http://www.chromium.org/developers/coding-style#TOC-File-headers',
195 files)]
196 return []
197
198
charujain9893e252017-09-14 13:33:22 +0200199def CheckNoFRIEND_TEST(input_api, output_api): # pylint: disable=invalid-name
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000200 """Make sure that gtest's FRIEND_TEST() macro is not used, the
201 FRIEND_TEST_ALL_PREFIXES() macro from testsupport/gtest_prod_util.h should be
202 used instead since that allows for FLAKY_, FAILS_ and DISABLED_ prefixes."""
203 problems = []
204
205 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.h'))
206 for f in input_api.AffectedFiles(file_filter=file_filter):
207 for line_num, line in f.ChangedContents():
208 if 'FRIEND_TEST(' in line:
209 problems.append(' %s:%d' % (f.LocalPath(), line_num))
210
211 if not problems:
212 return []
213 return [output_api.PresubmitPromptWarning('WebRTC\'s code should not use '
214 'gtest\'s FRIEND_TEST() macro. Include testsupport/gtest_prod_util.h and '
215 'use FRIEND_TEST_ALL_PREFIXES() instead.\n' + '\n'.join(problems))]
216
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000217
charujain9893e252017-09-14 13:33:22 +0200218def IsLintBlacklisted(blacklist_paths, file_path):
oprypin2aa463f2017-03-23 03:17:02 -0700219 """ Checks if a file is blacklisted for lint check."""
220 for path in blacklist_paths:
221 if file_path == path or os.path.dirname(file_path).startswith(path):
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100222 return True
223 return False
224
225
charujain9893e252017-09-14 13:33:22 +0200226def CheckApprovedFilesLintClean(input_api, output_api,
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000227 source_file_filter=None):
oprypin2aa463f2017-03-23 03:17:02 -0700228 """Checks that all new or non-blacklisted .cc and .h files pass cpplint.py.
charujain9893e252017-09-14 13:33:22 +0200229 This check is based on CheckChangeLintsClean in
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000230 depot_tools/presubmit_canned_checks.py but has less filters and only checks
231 added files."""
232 result = []
233
234 # Initialize cpplint.
235 import cpplint
236 # Access to a protected member _XX of a client class
237 # pylint: disable=W0212
238 cpplint._cpplint_state.ResetErrorCounts()
239
jbauchc4e3ead2016-02-19 00:25:55 -0800240 lint_filters = cpplint._Filters()
241 lint_filters.extend(BLACKLIST_LINT_FILTERS)
242 cpplint._SetFilters(','.join(lint_filters))
243
oprypin2aa463f2017-03-23 03:17:02 -0700244 # Create a platform independent blacklist for cpplint.
245 blacklist_paths = [input_api.os_path.join(*path.split('/'))
246 for path in CPPLINT_BLACKLIST]
kjellander@webrtc.org0fcaf992015-11-26 15:24:52 +0100247
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000248 # Use the strictest verbosity level for cpplint.py (level 1) which is the
oprypin2aa463f2017-03-23 03:17:02 -0700249 # default when running cpplint.py from command line. To make it possible to
250 # work with not-yet-converted code, we're only applying it to new (or
251 # moved/renamed) files and files not listed in CPPLINT_BLACKLIST.
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000252 verbosity_level = 1
253 files = []
254 for f in input_api.AffectedSourceFiles(source_file_filter):
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200255 # Note that moved/renamed files also count as added.
charujain9893e252017-09-14 13:33:22 +0200256 if f.Action() == 'A' or not IsLintBlacklisted(blacklist_paths,
oprypin2aa463f2017-03-23 03:17:02 -0700257 f.LocalPath()):
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000258 files.append(f.AbsoluteLocalPath())
mflodman@webrtc.org2a452092012-07-01 05:55:23 +0000259
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000260 for file_name in files:
261 cpplint.ProcessFile(file_name, verbosity_level)
262
263 if cpplint._cpplint_state.error_count > 0:
264 if input_api.is_committing:
oprypin8e58d652017-03-21 07:52:41 -0700265 res_type = output_api.PresubmitError
kjellander@webrtc.org51198f12012-02-21 17:53:46 +0000266 else:
267 res_type = output_api.PresubmitPromptWarning
268 result = [res_type('Changelist failed cpplint.py check.')]
269
270 return result
271
charujain9893e252017-09-14 13:33:22 +0200272def CheckNoSourcesAbove(input_api, gn_files, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700273 # Disallow referencing source files with paths above the GN file location.
274 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
275 re.MULTILINE | re.DOTALL)
276 file_pattern = input_api.re.compile(r'"((\.\./.*?)|(//.*?))"')
277 violating_gn_files = set()
278 violating_source_entries = []
279 for gn_file in gn_files:
280 contents = input_api.ReadFile(gn_file)
281 for source_block_match in source_pattern.finditer(contents):
282 # Find all source list entries starting with ../ in the source block
283 # (exclude overrides entries).
284 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
285 source_file = file_list_match.group(1)
286 if 'overrides/' not in source_file:
287 violating_source_entries.append(source_file)
288 violating_gn_files.add(gn_file)
289 if violating_gn_files:
290 return [output_api.PresubmitError(
291 'Referencing source files above the directory of the GN file is not '
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100292 'allowed. Please introduce new GN targets in the proper location '
293 'instead.\n'
ehmaldonado5b1ba082016-09-02 05:51:08 -0700294 'Invalid source entries:\n'
295 '%s\n'
296 'Violating GN files:' % '\n'.join(violating_source_entries),
297 items=violating_gn_files)]
298 return []
299
charujain9893e252017-09-14 13:33:22 +0200300def CheckNoMixingCAndCCSources(input_api, gn_files, output_api):
kjellander7439f972016-12-05 22:47:46 -0800301 # Disallow mixing .c and .cc source files in the same target.
302 source_pattern = input_api.re.compile(r' +sources \+?= \[(.*?)\]',
303 re.MULTILINE | re.DOTALL)
304 file_pattern = input_api.re.compile(r'"(.*)"')
305 violating_gn_files = dict()
306 for gn_file in gn_files:
307 contents = input_api.ReadFile(gn_file)
308 for source_block_match in source_pattern.finditer(contents):
309 c_files = []
310 cc_files = []
311 for file_list_match in file_pattern.finditer(source_block_match.group(1)):
312 source_file = file_list_match.group(1)
313 if source_file.endswith('.c'):
314 c_files.append(source_file)
315 if source_file.endswith('.cc'):
316 cc_files.append(source_file)
317 if c_files and cc_files:
318 violating_gn_files[gn_file.LocalPath()] = sorted(c_files + cc_files)
319 if violating_gn_files:
320 return [output_api.PresubmitError(
321 'GN targets cannot mix .cc and .c source files. Please create a '
322 'separate target for each collection of sources.\n'
323 'Mixed sources: \n'
324 '%s\n'
325 'Violating GN files:' % json.dumps(violating_gn_files, indent=2),
326 items=violating_gn_files.keys())]
327 return []
328
charujain9893e252017-09-14 13:33:22 +0200329def CheckNoPackageBoundaryViolations(input_api, gn_files, output_api):
ehmaldonado4fb97462017-01-30 05:27:22 -0800330 cwd = input_api.PresubmitLocalPath()
mbonadeiab587dc2017-05-12 04:13:31 -0700331 script_path = os.path.join('tools_webrtc', 'presubmit_checks_lib',
332 'check_package_boundaries.py')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200333 command = [sys.executable, script_path]
ehmaldonado4fb97462017-01-30 05:27:22 -0800334 command += [gn_file.LocalPath() for gn_file in gn_files]
335 returncode, _, stderr = _RunCommand(command, cwd)
336 if returncode:
337 return [output_api.PresubmitError(
338 'There are package boundary violations in the following GN files:\n\n'
339 '%s' % stderr)]
340 return []
341
charujain9893e252017-09-14 13:33:22 +0200342def CheckGnChanges(input_api, output_api):
ehmaldonado5b1ba082016-09-02 05:51:08 -0700343 source_file_filter = lambda x: input_api.FilterSourceFile(
344 x, white_list=(r'.+\.(gn|gni)$',))
345
346 gn_files = []
347 for f in input_api.AffectedSourceFiles(source_file_filter):
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200348 gn_files.append(f)
ehmaldonado5b1ba082016-09-02 05:51:08 -0700349
350 result = []
351 if gn_files:
charujain9893e252017-09-14 13:33:22 +0200352 result.extend(CheckNoSourcesAbove(input_api, gn_files, output_api))
353 result.extend(CheckNoMixingCAndCCSources(input_api, gn_files, output_api))
354 result.extend(CheckNoPackageBoundaryViolations(
ehmaldonado4fb97462017-01-30 05:27:22 -0800355 input_api, gn_files, output_api))
ehmaldonado5b1ba082016-09-02 05:51:08 -0700356 return result
357
charujain9893e252017-09-14 13:33:22 +0200358def CheckUnwantedDependencies(input_api, output_api):
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000359 """Runs checkdeps on #include statements added in this
360 change. Breaking - rules is an error, breaking ! rules is a
361 warning.
362 """
363 # Copied from Chromium's src/PRESUBMIT.py.
364
365 # We need to wait until we have an input_api object and use this
366 # roundabout construct to import checkdeps because this file is
367 # eval-ed and thus doesn't have __file__.
368 original_sys_path = sys.path
369 try:
kjellander@webrtc.orgaefe61a2014-12-08 13:00:30 +0000370 checkdeps_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
371 'buildtools', 'checkdeps')
372 if not os.path.exists(checkdeps_path):
373 return [output_api.PresubmitError(
374 'Cannot find checkdeps at %s\nHave you run "gclient sync" to '
375 'download Chromium and setup the symlinks?' % checkdeps_path)]
376 sys.path.append(checkdeps_path)
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000377 import checkdeps
378 from cpp_checker import CppChecker
379 from rules import Rule
380 finally:
381 # Restore sys.path to what it was before.
382 sys.path = original_sys_path
383
384 added_includes = []
385 for f in input_api.AffectedFiles():
386 if not CppChecker.IsCppFile(f.LocalPath()):
387 continue
388
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200389 changed_lines = [line for _, line in f.ChangedContents()]
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000390 added_includes.append([f.LocalPath(), changed_lines])
391
392 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
393
394 error_descriptions = []
395 warning_descriptions = []
396 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
397 added_includes):
398 description_with_path = '%s\n %s' % (path, rule_description)
399 if rule_type == Rule.DISALLOW:
400 error_descriptions.append(description_with_path)
401 else:
402 warning_descriptions.append(description_with_path)
403
404 results = []
405 if error_descriptions:
406 results.append(output_api.PresubmitError(
kjellandera7066a32017-03-23 03:47:05 -0700407 'You added one or more #includes that violate checkdeps rules.\n'
408 'Check that the DEPS files in these locations contain valid rules.\n'
409 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
410 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000411 error_descriptions))
412 if warning_descriptions:
413 results.append(output_api.PresubmitPromptOrNotify(
414 'You added one or more #includes of files that are temporarily\n'
415 'allowed but being removed. Can you avoid introducing the\n'
kjellandera7066a32017-03-23 03:47:05 -0700416 '#include? See relevant DEPS file(s) for details and contacts.\n'
417 'See https://cs.chromium.org/chromium/src/buildtools/checkdeps/ for '
418 'more details about checkdeps.',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000419 warning_descriptions))
420 return results
421
charujain9893e252017-09-14 13:33:22 +0200422def CheckCommitMessageBugEntry(input_api, output_api):
423 """Check that bug entries are well-formed in commit message."""
424 bogus_bug_msg = (
425 'Bogus BUG entry: %s. Please specify the issue tracker prefix and the '
426 'issue number, separated by a colon, e.g. webrtc:123 or chromium:12345.')
427 results = []
428 for bug in (input_api.change.BUG or '').split(','):
429 bug = bug.strip()
430 if bug.lower() == 'none':
431 continue
432 if ':' not in bug:
433 try:
434 if int(bug) > 100000:
435 # Rough indicator for current chromium bugs.
436 prefix_guess = 'chromium'
437 else:
438 prefix_guess = 'webrtc'
439 results.append('BUG entry requires issue tracker prefix, e.g. %s:%s' %
440 (prefix_guess, bug))
441 except ValueError:
442 results.append(bogus_bug_msg % bug)
443 elif not re.match(r'\w+:\d+', bug):
444 results.append(bogus_bug_msg % bug)
445 return [output_api.PresubmitError(r) for r in results]
446
447def CheckChangeHasBugField(input_api, output_api):
kjellanderd1e26a92016-09-19 08:11:16 -0700448 """Requires that the changelist have a BUG= field.
449
450 This check is stricter than the one in depot_tools/presubmit_canned_checks.py
451 since it fails the presubmit if the BUG= field is missing or doesn't contain
452 a bug reference.
453 """
454 if input_api.change.BUG:
455 return []
456 else:
457 return [output_api.PresubmitError(
458 'The BUG=[bug number] field is mandatory. Please create a bug and '
459 'reference it using either of:\n'
460 ' * https://bugs.webrtc.org - reference it using BUG=webrtc:XXXX\n'
461 ' * https://crbug.com - reference it using BUG=chromium:XXXXXX')]
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000462
charujain9893e252017-09-14 13:33:22 +0200463def CheckJSONParseErrors(input_api, output_api):
kjellander569cf942016-02-11 05:02:59 -0800464 """Check that JSON files do not contain syntax errors."""
465
466 def FilterFile(affected_file):
467 return input_api.os_path.splitext(affected_file.LocalPath())[1] == '.json'
468
469 def GetJSONParseError(input_api, filename):
470 try:
471 contents = input_api.ReadFile(filename)
472 input_api.json.loads(contents)
473 except ValueError as e:
474 return e
475 return None
476
477 results = []
478 for affected_file in input_api.AffectedFiles(
479 file_filter=FilterFile, include_deletes=False):
480 parse_error = GetJSONParseError(input_api,
481 affected_file.AbsoluteLocalPath())
482 if parse_error:
483 results.append(output_api.PresubmitError('%s could not be parsed: %s' %
484 (affected_file.LocalPath(), parse_error)))
485 return results
486
487
charujain9893e252017-09-14 13:33:22 +0200488def RunPythonTests(input_api, output_api):
kjellanderc88b5d52017-04-05 06:42:43 -0700489 def Join(*args):
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200490 return input_api.os_path.join(input_api.PresubmitLocalPath(), *args)
491
492 test_directories = [
Edward Lemur6d01f6d2017-09-14 17:02:01 +0200493 input_api.PresubmitLocalPath(),
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200494 Join('rtc_tools', 'py_event_log_analyzer'),
495 Join('rtc_tools'),
496 Join('audio', 'test', 'unittests'),
ehmaldonado4fb97462017-01-30 05:27:22 -0800497 ] + [
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200498 root for root, _, files in os.walk(Join('tools_webrtc'))
ehmaldonado4fb97462017-01-30 05:27:22 -0800499 if any(f.endswith('_test.py') for f in files)
Henrik Kjellander8d3ad822015-05-26 19:52:05 +0200500 ]
501
502 tests = []
503 for directory in test_directories:
504 tests.extend(
505 input_api.canned_checks.GetUnitTestsInDirectory(
506 input_api,
507 output_api,
508 directory,
509 whitelist=[r'.+_test\.py$']))
510 return input_api.RunTests(tests, parallel=True)
511
512
charujain9893e252017-09-14 13:33:22 +0200513def CheckUsageOfGoogleProtobufNamespace(input_api, output_api):
mbonadei38415b22017-04-07 05:38:01 -0700514 """Checks that the namespace google::protobuf has not been used."""
515 files = []
516 pattern = input_api.re.compile(r'google::protobuf')
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200517 proto_utils_path = os.path.join('rtc_base', 'protobuf_utils.h')
mbonadei38415b22017-04-07 05:38:01 -0700518 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
519 if f.LocalPath() in [proto_utils_path, 'PRESUBMIT.py']:
520 continue
521 contents = input_api.ReadFile(f)
522 if pattern.search(contents):
523 files.append(f)
524
525 if files:
526 return [output_api.PresubmitError(
527 'Please avoid to use namespace `google::protobuf` directly.\n'
528 'Add a using directive in `%s` and include that header instead.'
529 % proto_utils_path, files)]
530 return []
531
532
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200533def _LicenseHeader(input_api):
534 """Returns the license header regexp."""
535 # Accept any year number from 2003 to the current year
536 current_year = int(input_api.time.strftime('%Y'))
537 allowed_years = (str(s) for s in reversed(xrange(2003, current_year + 1)))
538 years_re = '(' + '|'.join(allowed_years) + ')'
539 license_header = (
540 r'.*? Copyright( \(c\))? %(year)s The WebRTC [Pp]roject [Aa]uthors\. '
541 r'All [Rr]ights [Rr]eserved\.\n'
542 r'.*?\n'
543 r'.*? Use of this source code is governed by a BSD-style license\n'
544 r'.*? that can be found in the LICENSE file in the root of the source\n'
545 r'.*? tree\. An additional intellectual property rights grant can be '
546 r'found\n'
547 r'.*? in the file PATENTS\. All contributing project authors may\n'
548 r'.*? be found in the AUTHORS file in the root of the source tree\.\n'
549 ) % {
550 'year': years_re,
551 }
552 return license_header
553
554
charujain9893e252017-09-14 13:33:22 +0200555def CommonChecks(input_api, output_api):
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000556 """Checks common to both upload and commit."""
niklase@google.comda159d62011-05-30 11:51:34 +0000557 results = []
tkchin42f580e2015-11-26 23:18:23 -0800558 # Filter out files that are in objc or ios dirs from being cpplint-ed since
559 # they do not follow C++ lint rules.
560 black_list = input_api.DEFAULT_BLACK_LIST + (
561 r".*\bobjc[\\\/].*",
Kári Tristan Helgason3fa35172016-09-09 08:55:05 +0000562 r".*objc\.[hcm]+$",
tkchin42f580e2015-11-26 23:18:23 -0800563 )
564 source_file_filter = lambda x: input_api.FilterSourceFile(x, None, black_list)
charujain9893e252017-09-14 13:33:22 +0200565 results.extend(CheckApprovedFilesLintClean(
tkchin42f580e2015-11-26 23:18:23 -0800566 input_api, output_api, source_file_filter))
Mirko Bonadei92ea95e2017-09-15 06:47:31 +0200567 results.extend(input_api.canned_checks.CheckLicense(
568 input_api, output_api, _LicenseHeader(input_api)))
phoglund@webrtc.org5d3713932013-03-07 09:59:43 +0000569 results.extend(input_api.canned_checks.RunPylint(input_api, output_api,
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100570 black_list=(r'^base[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200571 r'^build[\\\/].*\.py$',
572 r'^buildtools[\\\/].*\.py$',
kjellander38c65c82017-04-12 22:43:38 -0700573 r'^infra[\\\/].*\.py$',
Henrik Kjellander0779e8f2016-12-22 12:01:17 +0100574 r'^ios[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200575 r'^out.*[\\\/].*\.py$',
576 r'^testing[\\\/].*\.py$',
577 r'^third_party[\\\/].*\.py$',
kjellander@webrtc.org177567c2016-12-22 10:40:28 +0100578 r'^tools[\\\/].*\.py$',
kjellanderafd54942016-12-17 12:21:39 -0800579 # TODO(phoglund): should arguably be checked.
Henrik Kjellander90fd7d82017-05-09 08:30:10 +0200580 r'^tools_webrtc[\\\/]mb[\\\/].*\.py$',
581 r'^tools_webrtc[\\\/]valgrind[\\\/].*\.py$',
Henrik Kjellander14771ac2015-06-02 13:10:04 +0200582 r'^xcodebuild.*[\\\/].*\.py$',),
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200583 pylintrc='pylintrc'))
kjellander569cf942016-02-11 05:02:59 -0800584
nisse3d21e232016-09-02 03:07:06 -0700585 # TODO(nisse): talk/ is no more, so make below checks simpler?
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200586 # WebRTC can't use the presubmit_canned_checks.PanProjectChecks function since
587 # we need to have different license checks in talk/ and webrtc/ directories.
588 # Instead, hand-picked checks are included below.
Henrik Kjellander63224672015-09-08 08:03:56 +0200589
tkchin3cd9a302016-06-08 12:40:28 -0700590 # .m and .mm files are ObjC files. For simplicity we will consider .h files in
591 # ObjC subdirectories ObjC headers.
592 objc_filter_list = (r'.+\.m$', r'.+\.mm$', r'.+objc\/.+\.h$')
Henrik Kjellanderb4af3d62016-11-16 20:11:29 +0100593 # Skip long-lines check for DEPS and GN files.
594 build_file_filter_list = (r'.+\.gn$', r'.+\.gni$', 'DEPS')
tkchin3cd9a302016-06-08 12:40:28 -0700595 eighty_char_sources = lambda x: input_api.FilterSourceFile(x,
596 black_list=build_file_filter_list + objc_filter_list)
597 hundred_char_sources = lambda x: input_api.FilterSourceFile(x,
598 white_list=objc_filter_list)
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000599 results.extend(input_api.canned_checks.CheckLongLines(
tkchin3cd9a302016-06-08 12:40:28 -0700600 input_api, output_api, maxlen=80, source_file_filter=eighty_char_sources))
601 results.extend(input_api.canned_checks.CheckLongLines(
602 input_api, output_api, maxlen=100,
603 source_file_filter=hundred_char_sources))
604
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000605 results.extend(input_api.canned_checks.CheckChangeHasNoTabs(
606 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000607 results.extend(input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
608 input_api, output_api))
kjellandere5dc62a2016-12-14 00:16:21 -0800609 results.extend(input_api.canned_checks.CheckAuthorizedAuthor(
610 input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000611 results.extend(input_api.canned_checks.CheckChangeTodoHasOwner(
612 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200613 results.extend(CheckNativeApiHeaderChanges(input_api, output_api))
614 results.extend(CheckNoIOStreamInHeaders(input_api, output_api))
615 results.extend(CheckNoPragmaOnce(input_api, output_api))
616 results.extend(CheckNoFRIEND_TEST(input_api, output_api))
617 results.extend(CheckGnChanges(input_api, output_api))
618 results.extend(CheckUnwantedDependencies(input_api, output_api))
619 results.extend(CheckJSONParseErrors(input_api, output_api))
620 results.extend(RunPythonTests(input_api, output_api))
621 results.extend(CheckUsageOfGoogleProtobufNamespace(input_api, output_api))
Mirko Bonadei866d3372017-09-15 12:35:26 +0200622 results.extend(CheckOrphanHeaders(input_api, output_api))
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200623 results.extend(CheckNewlineAtTheEndOfProtoFiles(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000624 return results
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000625
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000626
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000627def CheckChangeOnUpload(input_api, output_api):
628 results = []
charujain9893e252017-09-14 13:33:22 +0200629 results.extend(CommonChecks(input_api, output_api))
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200630 results.extend(
631 input_api.canned_checks.CheckGNFormatted(input_api, output_api))
niklase@google.comda159d62011-05-30 11:51:34 +0000632 return results
633
kjellander@webrtc.orge4158642014-08-06 09:11:18 +0000634
andrew@webrtc.org2442de12012-01-23 17:45:41 +0000635def CheckChangeOnCommit(input_api, output_api):
niklase@google.com1198db92011-06-09 07:07:24 +0000636 results = []
charujain9893e252017-09-14 13:33:22 +0200637 results.extend(CommonChecks(input_api, output_api))
638 results.extend(VerifyNativeApiHeadersListIsValid(input_api, output_api))
niklase@google.com1198db92011-06-09 07:07:24 +0000639 results.extend(input_api.canned_checks.CheckOwners(input_api, output_api))
andrew@webrtc.org53df1362012-01-26 21:24:23 +0000640 results.extend(input_api.canned_checks.CheckChangeWasUploaded(
641 input_api, output_api))
642 results.extend(input_api.canned_checks.CheckChangeHasDescription(
643 input_api, output_api))
charujain9893e252017-09-14 13:33:22 +0200644 results.extend(CheckChangeHasBugField(input_api, output_api))
645 results.extend(CheckCommitMessageBugEntry(input_api, output_api))
kjellander@webrtc.org12cb88c2014-02-13 11:53:43 +0000646 results.extend(input_api.canned_checks.CheckTreeIsOpen(
647 input_api, output_api,
648 json_url='http://webrtc-status.appspot.com/current?format=json'))
niklase@google.com1198db92011-06-09 07:07:24 +0000649 return results
mbonadei74973ed2017-05-09 07:58:05 -0700650
651
charujain9893e252017-09-14 13:33:22 +0200652def CheckOrphanHeaders(input_api, output_api):
mbonadei74973ed2017-05-09 07:58:05 -0700653 # We need to wait until we have an input_api object and use this
654 # roundabout construct to import prebubmit_checks_lib because this file is
655 # eval-ed and thus doesn't have __file__.
656 error_msg = """Header file {} is not listed in any GN target.
657 Please create a target or add it to an existing one in {}"""
658 results = []
659 original_sys_path = sys.path
660 try:
661 sys.path = sys.path + [input_api.os_path.join(
662 input_api.PresubmitLocalPath(), 'tools_webrtc', 'presubmit_checks_lib')]
663 from check_orphan_headers import GetBuildGnPathFromFilePath
664 from check_orphan_headers import IsHeaderInBuildGn
665 finally:
666 # Restore sys.path to what it was before.
667 sys.path = original_sys_path
668
669 for f in input_api.AffectedSourceFiles(input_api.FilterSourceFile):
mbonadeia644ad32017-05-10 05:21:55 -0700670 if f.LocalPath().endswith('.h') and f.Action() == 'A':
mbonadei74973ed2017-05-09 07:58:05 -0700671 file_path = os.path.abspath(f.LocalPath())
672 root_dir = os.getcwd()
673 gn_file_path = GetBuildGnPathFromFilePath(file_path, os.path.exists,
674 root_dir)
675 in_build_gn = IsHeaderInBuildGn(file_path, gn_file_path)
676 if not in_build_gn:
677 results.append(output_api.PresubmitError(error_msg.format(
678 file_path, gn_file_path)))
679 return results
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200680
681
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200682def CheckNewlineAtTheEndOfProtoFiles(input_api, output_api):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200683 """Checks that all .proto files are terminated with a newline."""
684 error_msg = 'File {} must end with exactly one newline.'
685 results = []
686 source_file_filter = lambda x: input_api.FilterSourceFile(
687 x, white_list=(r'.+\.proto$',))
688 for f in input_api.AffectedSourceFiles(source_file_filter):
689 file_path = f.LocalPath()
690 with open(file_path) as f:
691 lines = f.readlines()
Mirko Bonadeia730c1c2017-09-18 11:33:13 +0200692 if len(lines) > 0 and not lines[-1].endswith('\n'):
Mirko Bonadei960fd5b2017-06-29 14:59:36 +0200693 results.append(output_api.PresubmitError(error_msg.format(file_path)))
694 return results